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)
Since Spring 4.1 you could set up property values just in code by using org.springframework.test.context.TestPropertySource
annotation on Unit Tests class level. You could use this approach even for injecting properties into dependent bean instances
For example
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = FooTest.Config.class)@TestPropertySource(properties = { "some.bar.value=testValue",})public class FooTest { @Value("${some.bar.value}") String bar; @Test public void testValueSetup() { assertEquals("testValue", bar); } @Configuration static class Config { @Bean public static PropertySourcesPlaceholderConfigurer propertiesResolver() { return new PropertySourcesPlaceholderConfigurer(); } }}
Note: It's necessary to have instance of org.springframework.context.support.PropertySourcesPlaceholderConfigurer
in Spring context
Edit 24-08-2017: If you are using SpringBoot 1.4.0 and later you could initialize tests with @SpringBootTest
and @SpringBootConfiguration
annotations. More info here
In case of SpringBoot we have following code
@SpringBootTest@SpringBootConfiguration@RunWith(SpringJUnit4ClassRunner.class)@TestPropertySource(properties = { "some.bar.value=testValue",})public class FooTest { @Value("${some.bar.value}") String bar; @Test public void testValueSetup() { assertEquals("testValue", bar); }}
Don't abuse private fields get/set by reflection
Using reflection as that is done in several answers here is something that we could avoid.
It brings a small value here while it presents multiple drawbacks :
- we detect reflection issues only at runtime (ex: fields not existing any longer)
- We want encapsulation but not a opaque class that hides dependencies that should be visible and make the class more opaque and less testable.
- it encourages bad design. Today you declare a
@Value String field
. Tomorrow you can declare5
or10
of them in that class and you may not even be straight aware that you decrease the design of the class. With a more visible approach to set these fields (such as constructor) , you will think twice before adding all these fields and you will probably encapsulate them into another class and use@ConfigurationProperties
.
Make your class testable both unitary and in integration
To be able to write both plain unit tests (that is without a running spring container) and integration tests for your Spring component class, you have to make this class usable with or without Spring.
Running a container in an unit test when it is not required is a bad practice that slows down local builds : you don't want that.
I added this answer because no answer here seems to show this distinction and so they rely on a running container systematically.
So I think that you should move this property defined as an internal of the class :
@Componentpublic class Foo{ @Value("${property.value}") private String property; //...}
into a constructor parameter that will be injected by Spring :
@Componentpublic class Foo{ private String property; public Foo(@Value("${property.value}") String property){ this.property = property; } //... }
Unit test example
You can instantiate Foo
without Spring and inject any value for property
thanks to the constructor :
public class FooTest{ Foo foo = new Foo("dummyValue"); @Test public void doThat(){ ... }}
Integration test example
You can injecting the property in the context with Spring Boot in this simple way thanks to the properties
attribute of @SpringBootTest
:
@SpringBootTest(properties="property.value=dummyValue")public class FooTest{ @Autowired Foo foo; @Test public void doThat(){ ... } }
You could use as alternative @TestPropertySource
but it adds an additional annotation :
@SpringBootTest@TestPropertySource(properties="property.value=dummyValue")public class FooTest{ ...}
With Spring (without Spring Boot), it should be a little more complicated but as I didn't use Spring without Spring Boot from a long time I don't prefer say a stupid thing.
As a side note : if you have many @Value
fields to set, extracting them into a class annotated with @ConfigurationProperties
is more relevant because we don't want a constructor with too many arguments.