Spring Dynamic Property
How to set a property value in tests by the standard spring mechanics
In the Spring Framework we have an annotation TestPropertySource
which allows define a final property value:
@SpringBootTest
@ExtendWith(SpringExtension.class)
@TestPropertySource(properties = "my.variable=12345")
class FinalPropertyValueTest {
@Value("${my.variable}")
private String variable;
@Test
void testVariableValue() {
assertThat(variable).isEqualTo("12345");
}
}
On this way we have a small limitation - we cannot define a dynamic value of properties in this annotation, even use of a static method not allowed here.
How to define a dynamic value of properties in tests
spring-dynamic-property
supports an ability to define a dynamic value of properties and declare static property provider methods in your test class.
All that you need to use the spring-dynamic-property
is the next dependency:
<dependency>
<groupId>com.jupiter-tools</groupId>
<artifactId>spring-dynamic-property</artifactId>
<version>0.1</version>
</dependency>
Let’s look at the simple example of using a dynamic value in properties:
Simple example
@SpringBootTest
@ExtendWith(SpringExtension.class)
class SimpleTest {
@DynamicTestProperty
private static TestPropertyValues props() {
return TestPropertyValues
.of("variable=" + (int) Math.sqrt(64));
}
@Value("${variable}")
private int variable;
@Test
void testSqrt() {
assertThat(variable).isEqualTo(8);
}
}
Example with the using of TestContainers
A most primary case to use dynamic properties is a TestContainers initialization in your spring-boot applications.
Let’s consider an example of initialization standard spring-boot properties after start Redis test container:
@SpringBootTest
@Testcontainers
@ExtendWith(SpringExtension.class)
class RedisTestcontainersTest {
private static final Integer REDIS_PORT = 6379;
@Container
private static GenericContainer redis = new GenericContainer("redis:latest")
.withExposedPorts(REDIS_PORT);
@DynamicTestProperty
private static TestPropertyValues props() {
return TestPropertyValues.of("spring.redis.host=" + redis.getContainerIpAddress(),
"spring.redis.port=" + redis.getMappedPort(REDIS_PORT));
}
@Autowired
private RedisTemplate redisTemplate;
@Test
void readWriteValueByRedisTemplate() {
String key = "test";
String value = "sabracadabra";
// Act
redisTemplate.opsForValue().set(key, value);
// Assert
assertThat(redisTemplate.opsForValue().get(key)).isEqualTo(value);
}
}
Include dynamic property in meta-annotations
If you need to make you own annotation with dynamic property declaration then you can specify a file with @DynamicProperty
methods by the using of @IncludeDynamicProperty
annotation as you can see in the example below:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@IncludeDynamicProperty(DynamicPropertyHolder.class)
public @interface AnnotationWithDynamicProperty {
}
the file with dynamic properties:
public class DynamicPropertyHolder {
@DynamicTestProperty
private static TestPropertyValues property(){
return TestPropertyValues.of("key=12345");
}
}
and now you can use this annotation in your tests to set this property:
@SpringBootTest
@AnnotationWithDynamicProperty
class MetaAnnotationTest {
@Value("${key}")
private String key;
@Test
void checkPropertyValueInjectInAnnotation() {
assertThat(key).isEqualTo("12345");
}
}