Java 在集成测试中重写@Value

Java 在集成测试中重写@Value,java,spring,integration-testing,spring-test,Java,Spring,Integration Testing,Spring Test,对于我的一个Springbean(比如应用程序类),我使用@value注释从属性文件(prop.properties)获取属性值(my.property.flag=true/false)。那很好用 我需要编写一个集成测试(比如ApplicationIt类),其中我需要使用属性的两个值进行测试,即true和false 在我的属性文件中,属性的值设置为true。是否可以从集成测试中将该值动态设置为false 比如说, 道具属性: my.property.flag=true @Com

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

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

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

比如说,

道具属性:

    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?
    }
应用程序类文件:

    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?
    }
集成测试:

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

可以按如下方式指定测试类上的测试属性:

@RunWith(SpringRunner.class)
@TestPropertySource(properties = {"spring.main.banner-mode=off", "my.property.flag=false"})
public class MyTest {
因为Spring有一个完整的属性重写层次结构,所以它工作得很好,缺点是您需要为不同的值使用不同的测试类。如果您使用的是SpringBoot,那么还有另一个注释提供了相同的功能,但也有更多用于配置测试环境的选项。例如:

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

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

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

@Component
class Application {

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

这使得使用mock或test值就像传递参数一样简单。

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

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

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

我对此进行了一段时间的调试,并找到了一种覆盖属性的简洁方法。如果您需要对应用程序上下文进行一些编程初始化,例如像在这种情况下一样注册属性源,那么它非常有用,但不仅仅是这样。以下方法使用
ContextConfiguration
初始值设定项

弹簧靴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));
        }
    }
}
@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);
        }
    }
}
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=webEnvironment.RANDOM_端口,属性={“management.PORT=0”})
@ContextConfiguration(初始值设定项=AbstractIntegrationTest.Initializer.class)
@肮脏的环境
公共抽象类AbstractIntegrationTest{
私有静态int REDIS_端口=6379;
@阶级规则
公共静态GenericContainer redis=新的GenericContainer(“redis:3.0.6”)。具有公开的端口(redis\u端口);
公共静态类初始值设定项实现ApplicationContextInitializer{
@凌驾
公共无效初始化(ConfigurableApplicationContext ctx){
TestPropertySourceUtils.将线性属性添加到环境中(ctx,
“spring.redis.host=“+redis.getContainerIpAddress(),
“spring.redis.port=“+redis.getMappedPort(redis_-port));
}
}
}
弹簧靴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) {
            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(ctx,
                "spring.redis.host=" + redis.getContainerIpAddress(),
                "spring.redis.port=" + redis.getMappedPort(REDIS_PORT));
        }
    }
}
@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);
        }
    }
}
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=webEnvironment.RANDOM_端口,属性={“management.PORT=0”})
@ContextConfiguration(初始值设定项=AbstractIntegrationTest.Initializer.class)
@肮脏的环境
公共抽象类AbstractIntegrationTest{
私有静态int REDIS_端口=6379;
@阶级规则
公共静态GenericContainer redis=新的GenericContainer(“redis:3.0.6”)。具有公开的端口(redis\u端口);
公共静态类初始值设定项实现ApplicationContextInitializer{
@凌驾
公共无效初始化(ConfigurableApplicationContext ctx){
TestPropertyValue.of(
“spring.redis.host:”+redis.getContainerIpAddress(),
spring.redis.port:“+redis.getMappedPort(redis_-port))
.applyTo(ctx);
}
}
}

您可以使用测试覆盖创建单独的属性文件。此属性文件中有许多属性,我的要求仅适用于单个属性。我认为仅仅为这一个属性创建一个新的属性文件是不够明智的。您可能想检查这一点是的,但是如何在集成测试中以动态方式重写-例如,以便在测试执行期间确定和设置my.propery.flag