在集成测试中重写@Value

Overriding @Value in Integration Test

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

我需要编写一个集成测试(比如 ApplicationIt class),我需要用 属性 的两个值进行测试,即 true 和 false。

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

例如,

prop.properties:

    my.property.flag=true

申请class文件:

    @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?
    }

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

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

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

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

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

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

@Component
class Application {

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

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

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

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

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

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

Spring 引导 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 启动示例 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);
        }
    }
}