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
How to Disable Spring Security for Particular Url
How to Solve Maven 2.6 Resource Plugin Dependency
How to Properly Re-Run Spring Boot Application from Eclipse
Where Does Gradle Save Dependencies' Jars
Android - Running a Background Task Every 15 Minutes, Even When Application Is Not Running
How to Add Multiple Components to a Jframe
Geting Http Status 400 - Required Multipartfile Parameter 'File' Is Not Present in Spring
How to Send Parallel Get Requests and Wait for Result Responses
How to Process Lines of a File in Parallel
How to Combine Date and Time into a Single Object
How to Get Full Name of an Employee by Joining First, Middle and Last Name Considering Null in Java
Spring JPA - Why Is My Findall Returning Null When There Is Data in My Database
Aws Lambda: How to Extract a Tgz File in a S3 Bucket and Put It in Another S3 Bucket
How to Get a Resource Id With a Known Resource Name
Java.Lang.Illegalstateexception: Fragment Not Attached to a Context