Populating Spring @Value During Unit Test

Populating Spring @Value during Unit Test

If possible I would try to write those test without Spring Context. If you create this class in your test without spring, then you have full control over its fields.

To set the @value field you can use Springs ReflectionTestUtils - it has a method setField to set private fields.

@see JavaDoc: ReflectionTestUtils.setField(java.lang.Object, java.lang.String, java.lang.Object)

How to Pass @Value to a @Component for Spring Unit Testing

You can inject the @Value in test class by ReflectionTestUtils. Load container only in case of Controllers. For writing test cases for services and dao you don't need to load the spring container.

public class TestClass{
private @InjectsMock ServiceClass service;

@BeforeAll
public void setUp(){
ReflectionTestUtils.setField(service, "someString", "someValue");
}

//your test cases over here.
}

JUnit testing of spring boot @value variable

I assume, you have already managed to mock 'userType', so in order to mock 'userTypeSpecific', you can use Spring's ReflectionTestUtils.setField method to mock @value variables.
You can find details here:
https://www.briandupreez.net/2011/04/little-spring-gem-reflectiontestutils.html

@Value resolving to null when running things from unit test

Because all the @MockitoJUnitRunner, @InjectMocks and @Mock are Mockito stuffs and they do not know anything about Spring. Hence they do not understand what @Value does and will not inject its value. The spring container even do not start in you case.

If you are using spring-boot and want Spring to inject this value , you can consider using spring-boot-starter-test starter and use its @MockBean to configure the Mockito mock :

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestBidService {

@Autowired
private BidServiceImpl bidService;

@MockBean
RestTemplate restTemplate;

@Test
public void testFindAllReturnsListOfBids(){
///
}
}

But it is an integration test as it will start up the whole spring container , so it is slower than the true unit test.

If you want the test to run as fast as possible , don't rely on Spring to inject that value for you. Simply set up by yourself:

@RunWith(MockitoJUnitRunner.class)
public class TestBidService {

@Mock
RestTemplate restTemplate;

@Test
public void testFindAllReturnsListOfBids(){
BidServiceImpl bidService = new BidServiceImpl(restTemplate , "http://127.0.0.1/");
////
}

}

Spring Boot Unit Test @Value from .properties File gives NullPointerException

Thanks to @shazin's answer and some of my own research I've been able to solve the problem.

Basically, there needs to be compatibility between the test runner class specified in @RunWith and the annotations for the Mockito mocks. We want to test the Service class:

Service Class:

@Component
public class Service {
@Autowired
Environment environment;

public String getProperty() {
return environment.getProperty("prop");
}
}

If you're using @RunWith(MockitoJUnitRunner.class), you can use the @InjectMocks and @Mock annotations like below. Whatever is @Autowired in Service will be auto-wired with the mocks:

Test Class with MockitoJUnitRunner:

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
@InjectMocks
Service service;
@Mock
Environment mockEnvironment;

@Before
public void before() {
Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
}
}

But you can't auto-wire anything in the test class itself. That requires a Spring Context (a Spring Context is needed to manage the beans which get auto-wired into objects). That's where @RunWith(SpringRunner.class) comes into the picture. You can use it to run a test case with a dedicated Spring context (you'll notice the test case logs showing a new Spring application being booted up for every test class with the @RunWith(SpringRunner.class) annotation). You'll also need to provide the Configuration details with the @SpringBootTest annotation.

The caveat is that a test class with @RunWith(SpringRunner.class) won't understand the @InjectMocks and @Mock annotations; you'll have to use the @MockBean annotation. This will effectively modify the Spring context by replacing beans with their mocks; anything with the @Autowired annotation will get auto-wired with the mock beans automatically:

Test Class with SpringRunner:

@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class ServiceTest {
@Autowired
Service service;

@MockBean
Environment mockEnvironment;

@Before
public void before() {
Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
}
}

So...using the @RunWith(SpringRunner.class) didn't achieve anything except change the names of the annotations (@InjectMocks -> @Autowired, and @Mock -> @MockBean), right? Wrong. Using SpringRunner gives you the power of auto-wiring components within your test case. So if you want to use an actual Environment (not a mock one), you can do that as well; just auto-wire it in from the dedicated Spring context:

Test Class with SpringRunner and @Autowired Environment:

@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class ServiceTest {
@Autowired
Service service;

@Autowired
Environment environment;

@Test
public void testServiceGetProperty() {
assertEquals(environment.getProperty("prop"), service.getProperty("prop");
}

}

And that solves the problem.

Value annotation not working in Junit test

Following works for me. It picks up value from the application.properties file.

@RunWith(SpringRunner.class)
@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
public class ValueAnnotationTest {

@Value("${myUrl}")
private String myUrl;

@Test
public void test1() throws Exception {
assertThat(myUrl).isEqualTo("http://test.com");
}
}

From Spring Boot docs:

Using ConfigFileApplicationContextInitializer alone does not provide
support for @Value("${…​}") injection. Its only job is to ensure that
application.properties files are loaded into Spring’s Environment. For
@Value support, you need to either additionally configure a
PropertySourcesPlaceholderConfigurer or use @SpringBootTest, which
auto-configures one for you.

Spring Boot unit test executing the main class

I ran into this myself. Its a counter-intuitive way Spring works. I solved it by putting my test application in a package outside of the package which has the main application.

So, if your main application is my.app.SampleMain, try putting your test class in my.test.SampleTest.



Related Topics



Leave a reply



Submit