Value Annotation Not Working in Junit Test

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.

SpringBoot - @Value is not working during JUnit Test for custom spring config location

Standard Spring Boot locations

If you want spring-boot's application.properties to be loaded, you should launch the unit test with Spring Boot (using @SpringApplicationConfiguration):

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { AppConfig.class })
public class FooServiceTest {
@Test
public void test...
}

The application.yml should be under /config or root in the classpath.

See Spring Doc:

SpringApplication will load properties from application.properties
files in the following locations and add them to the Spring
Environment:

  • A /config subdirectory of the current directory.
  • The current directory
  • A classpath /config package
  • The classpath root

Specify additional locations (exemple when executed from unit tests)

Normally, you could have used PropertySource, however even though it allows to load configuration files from other locations, it will not work for injected (@Value) properties.

You may however specify the spring.config.location environment variable in a static bloc:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { AppConfig.class })
public class FooServiceTest {
static {
//if the file is NOT in the classpath
System.setProperty("spring.config.location", "file:///path/to/application.yml");

//if the file is in the classpath
//System.setProperty("spring.config.location", "classpath:/path/in/classpath/application.yml");
}

@Test
public void test...
}

Run tests from Gradle

According to this you may do this:

$ gradle test -Dspring.config.location=file:///path/to/application.yaml

Or

$ SPRING_CONFIG_LOCATION=file:///path/to/application.yaml gradle test

Or add a task to define the systemProperty:

task extconfig {
run { systemProperty "spring.config.location", "file:///path/to/application.yaml" }
}

test.mustRunAfter extconfig

I can get a Spring @Value at one of my test classes, but can't at other

Finally i could solve it, adding the annotation @TestInstance(Lifecycle.PER_CLASS).

This way:

@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
@TestInstance(Lifecycle.PER_CLASS)
@Component
public class TestPropertiesProvider {
...
...

I really don't know how is this working, but solved me problem. I would really appreciate if somebody can explain it to me! Searching at Docs says that my instance will be just one of TestPropertiesProvider for all my tests, but that doesn't explain the problem I got.

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.

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)

Mock @Value in SpringBoot unit test not working

You can use @SpringBootTest without running the whole application by passing it the class that contains the @Value but you have to use Spring's extension @ExtendWith({SpringExtension.class}) which is included inside @SpringBootTest and by that using Spring's MockBean instead of @Mock and @Autowired for autowiring the bean like this:

@SpringBootTest(classes = FeatureFlagService.class)
class FeatureFlagServiceTests {

@MockBean
RestTemplate restTemplate;

@Autowired
FeatureFlagService featureFlasgService;

@Test
void propertyTest(){
Assertions.assertEquals(featureFlasgService.URL_FEATURE_FLAGS, "http://endpoint");
}

@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/");
////
}

}

Resolve Spring @Value expression in JUnit tests

Your test @Configuration class is missing an instance of PropertyPlaceholderConfigurer and that's why Spring does not know how to resolve those expressions; add a bean like the following to your SpringConfiguration class

@org.springframework.context.annotation.Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setIgnoreResourceNotFound(true);
return ppc;
}

and move it to a separate class and use

@ContextConfiguration(classes=SpringConfiguration.class)

to be more specific when running your test.



Related Topics



Leave a reply



Submit