如何让Spock Specification基于Spring application.properties属性条件执行?
如何让Spock Specification基于Spring配置属性条件执行?
背景
- 项目基于Java 11与Spring Boot 2.6.6开发
- 部分功能依赖
application.properties中的特定属性启用,相关Spring组件通过@ConditionalOnProperty注解实现条件加载 - 集成测试采用Groovy语言及Spock框架(版本2.1-groovy-3.0)编写
问题
能否让Spock Specification根据Spring的application.properties中的属性实现条件执行?
可行方案
Spock的@Requires注解支持条件测试,但默认无法直接访问Spring配置上下文。以下两种方案可以实现基于配置属性的条件判断:
方案一:直接读取配置文件(简单场景适用)
这种方式直接读取classpath下的配置文件,解析目标属性值,适合配置仅来自本地文件的场景:
import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import spock.lang.Requires import spock.lang.Specification @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles('integration') @ContextConfiguration(classes = TestSpringBootApplication) @Requires(reason = '仅当AAA功能启用时执行此测试', value = { isFeatureAAAEnabled() }) class ConditionalSpec extends Specification { // @Autowired // private AAAFeatureComponent aaaFeatureComponent def "one plus one should equal two"() { expect: 1 + 1 == 2 } private static boolean isFeatureAAAEnabled() { def properties = new Properties() // 加载默认配置 loadProperties(properties, "application.properties") // 加载active profile对应的配置(覆盖默认值) loadProperties(properties, "application-integration.properties") // 读取目标属性,默认返回false return Boolean.parseBoolean(properties.getProperty("feature.aaa.enabled", "false")) } private static void loadProperties(Properties properties, String fileName) { def resource = ConditionalSpec.class.getClassLoader().getResource(fileName) resource?.withInputStream { properties.load(it) } } }
方案二:利用Spring Environment(全配置来源支持)
如果配置来自环境变量、系统属性或远程配置中心,直接读取文件的方式会失效。此时可以自定义Spock扩展,结合Spring的Environment实现条件判断:
- 自定义扩展注解:
import org.spockframework.runtime.extension.ExtensionAnnotation import java.lang.annotation.* @Retention(RetentionPolicy.RUNTIME) @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(RequiresPropertyExtension) @interface RequiresProperty { String value() // 目标属性键名 String havingValue() default "true" // 属性预期值 String reason() default "属性不满足条件,跳过测试" }
- 实现扩展逻辑:
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension import org.spockframework.runtime.model.FeatureInfo import org.spockframework.runtime.model.SpecInfo import org.springframework.context.ApplicationContext import org.springframework.test.context.TestContextManager class RequiresPropertyExtension extends AbstractAnnotationDrivenExtension<RequiresProperty> { @Override void visitSpecAnnotation(RequiresProperty annotation, SpecInfo spec) { checkProperty(annotation, spec) } @Override void visitFeatureAnnotation(RequiresProperty annotation, FeatureInfo feature) { checkProperty(annotation, feature.parent) } private void checkProperty(RequiresProperty annotation, SpecInfo spec) { try { // 获取Spring应用上下文 def testContextManager = new TestContextManager(spec.reflection) ApplicationContext context = testContextManager.testContext.applicationContext def propertyValue = context.environment.getProperty(annotation.value()) // 判断属性值是否符合预期 if (!annotation.havingValue().equals(propertyValue)) { spec.skipped = true spec.skipReason = annotation.reason() } } catch (Exception e) { // 上下文初始化失败时跳过测试 spec.skipped = true spec.skipReason = "Spring上下文初始化失败:${e.message}" } } }
- 在测试类中使用自定义注解:
import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import spock.lang.Specification @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles('integration') @ContextConfiguration(classes = TestSpringBootApplication) @RequiresProperty(value = "feature.aaa.enabled", havingValue = "true", reason = "AAA功能未启用,跳过测试") class ConditionalSpec extends Specification { def "one plus one should equal two"() { expect: 1 + 1 == 2 } }
方案对比
- 方案一实现简单,无需额外代码,但仅能读取本地配置文件,无法处理环境变量、系统属性等外部配置。
- 方案二通过Spring
Environment获取属性,支持所有Spring兼容的配置来源,灵活性更高,但需要自定义Spock扩展。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

