12

For one of my Spring beans(say Application class), I'm fetching the value of a property(my.property.flag=true/false) from a properties file(prop.properties) using @Value annotation. That works perfectly fine.

I need to write an integration test(say ApplicationIt class) where I need to test with both the values of the property i.e. for both true and false.

In my properties file, the value of the property is set to true. Is it possible to set the value dynamically to false from my Integration test?

For Example,

prop.properties:

    my.property.flag=true

Application class file:

    @Component
    class Application {
        //This value is fetched from properties file
        //the value is set to true.
        @Value(${my.property.flag})
        private String isTrue;
        ......
        ..........
    }

Integration Test:

    class ApplicationIT {
        //how can I set the value of isTrue here to false?
    }
Christian Ascone
  • 1,117
  • 8
  • 14
Sandy
  • 459
  • 2
  • 6
  • 19
  • You can create a separate properties file with test overrides. – shmosel Jun 19 '17 at 21:04
  • there are many properties within this properties file and the requirement that I have is only for a single property. I don't think its wise enough to create a new property file just for this single property. – Sandy Jun 19 '17 at 21:11
  • You want probably check this https://stackoverflow.com/questions/16478679/update-field-annotated-with-value-in-runtime – Sergei Sirik Jun 19 '17 at 21:15
  • https://stackoverflow.com/questions/17353327/populating-spring-value-during-unit-test – shmosel Jun 19 '17 at 21:18

4 Answers4

21

You can specify test properties on the test class as follows:

@RunWith(SpringRunner.class)
@TestPropertySource(properties = {"spring.main.banner-mode=off", "my.property.flag=false"})
public class MyTest {

Since Spring has a whole hierarchy of property overrides, this works pretty well, the downside being you need separate test classes for different values. If you're using Spring Boot, there's another annotation that provides the same functionality but also has more options for configuring your test environment. Example:

@SpringBootTest(properties = {"spring.main.banner-mode=off", "my.property.flag=false"})

Again, you will need separate test classes to handle hard-coded test properties.

ngreen
  • 1,559
  • 13
  • 22
10

I was bugged with this for a while and found this neat way to override the properties. It is quite useful if you need some programmatic initialization of the application context such as registering property sources like in that case but not only. The following approach uses ContextConfiguration's initializers.

example for Spring Boot 1.5.x :

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"management.port=0"})
@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
@DirtiesContext
public abstract class AbstractIntegrationTest {

    private static int REDIS_PORT = 6379;

    @ClassRule
    public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(REDIS_PORT);

    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext ctx) {
            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(ctx,
                "spring.redis.host=" + redis.getContainerIpAddress(),
                "spring.redis.port=" + redis.getMappedPort(REDIS_PORT));
        }
    }
}

example for Spring Boot 2.x :

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"management.port=0"})
@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
@DirtiesContext
public abstract class AbstractIntegrationTest {

    private static int REDIS_PORT = 6379;

    @ClassRule
    public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(REDIS_PORT);

    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext ctx) {
            TestPropertyValues.of(
                "spring.redis.host:" + redis.getContainerIpAddress(),
                "spring.redis.port:" + redis.getMappedPort(REDIS_PORT))
                .applyTo(ctx);
        }
    }
}
magiccrafter
  • 5,175
  • 1
  • 56
  • 50
9

I want to mention good old reflection way. You can use spring provided utility class for it after you wired in your component:

ReflectionTestUtils.setField(component, "isTrue", true)

You can change it to any value you want in consequent tests

dev4Fun
  • 990
  • 9
  • 18
2

Preferably, use constructor injection instead of field injection:

@Component
class Application {

    Application(@Value("${my.property.flag}") boolean flag) {
        ...
    }
}

This makes using mocks or test values as simple as passing an argument.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • Yes but how do you then override in a dynamic way in an integration test - i.e. so that my.propery.flag could be determined and set during test execution – Tom Chamberlain Oct 29 '19 at 10:53