【问题标题】:Overriding @Value in Integration Test在集成测试中覆盖 @Value
【发布时间】:2017-11-22 05:21:47
【问题描述】:

对于我的一个 Spring bean(比如应用程序类),我使用 @Value 注释从属性文件(prop.properties)中获取属性的值(my.property.flag=true/false)。效果很好。

我需要编写一个集成测试(比如 ApplicationIt 类),我需要在其中测试属性的值,即真假。

在我的属性文件中,属性的值设置为 true。是否可以从我的集成测试中将值动态设置为 false?

例如,

prop.properties:

    my.property.flag=true

应用类文件:

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

集成测试:

    class ApplicationIT {
        //how can I set the value of isTrue here to false?
    }

【问题讨论】:

标签: java spring integration-testing spring-test


【解决方案1】:

我被这个问题困扰了一段时间,发现这种巧妙的方法可以覆盖属性。如果您需要对应用程序上下文进行一些编程初始化,例如在这种情况下注册属性源,这非常有用,但不仅如此。以下方法使用ContextConfigurationinitializers

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));
        }
    }
}

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);
        }
    }
}

【讨论】:

    【解决方案2】:

    您可以在测试类上指定测试属性,如下所示:

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

    由于 Spring 具有完整的属性覆盖层次结构,因此效果很好,缺点是您需要针对不同的值使用单独的测试类。如果您使用的是 Spring Boot,还有另一个注解提供了相同的功能,但也有更多选项来配置您的测试环境。示例:

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

    同样,您需要单独的测试类来处理硬编码的测试属性。

    【讨论】:

      【解决方案3】:

      我想提一下很好的旧反射方式。连接组件后,您可以使用 spring 提供的实用程序类:

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

      您可以在后续测试中将其更改为您想要的任何值

      【讨论】:

        【解决方案4】:

        最好使用构造函数注入而不是字段注入:

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

        这使得使用模拟或测试值就像传递参数一样简单。

        【讨论】:

        • 是的,但是如何在集成测试中以动态方式覆盖 - 即在测试执行期间可以确定和设置 my.propery.flag
        猜你喜欢
        • 2016-06-15
        • 2019-10-31
        • 1970-01-01
        • 1970-01-01
        • 2011-12-26
        • 2013-10-14
        • 1970-01-01
        • 2018-10-18
        • 1970-01-01
        相关资源
        最近更新 更多