基于 Spock 规范的 SpringBootTest 的奇怪行为

Weird behavior of SpringBootTest based Spock specifications

鉴于我的 Web 应用程序也有 Spring 包含 EvaluationHandler 的集成管道,我想模拟业务处理失败。我创建了以下方面:

@Aspect
class FailureSimulator {
    @Before("execution(* *..*EvaluationHandler+.handle(..))")
    static void fail() {
        simulateBusinessProcessingFailure();
    }

    private static void simulateBusinessProcessingFailure() {
        throw new DataIntegrityViolationException("Simulated failure.");
    }
}

EvaluationHandlerorg.springframework.integration.dsl.support.GenericHandler

我创建了导入 FailureSimulator

的 Spock 规范
@SpringBootTest
@DirtiesContext
@Import(FailureSimulator)
class JmsSimulatedBusinessFailureSpec extends Specification {
    private static final String ACTIVEMQ_DLQ = "ActiveMQ.DLQ"
    @Autowired
    JmsTemplate jmsTemplate

    def "business processing failure"() {
        when: "I send a message to JMS upstream queue"

        jmsTemplate.convertAndSend(UPSTREAM_EVENT_QUEUE, message)

        then: "message is retried 6 times and parked in DLQ"
        jmsTemplate.setReceiveTimeout(25000L)
        def msg = jmsTemplate.receive(ACTIVEMQ_DLQ) as ActiveMQTextMessage
        msg != null
    }
}

当我 运行 这个 Specification 单独时,它有效

但是,当我 运行 它又一个 Specification 这是基于 AbstractResourceProcessorSpec:

@SpringBootTest
abstract class AbstractResourceProcessorSpec extends Specification {}

测试失败,因为 FailureSimulator 没有启动。

即使我在日志中看到 org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public static void foo.FailureSimulator.fail() 也没有执行

我也在查看 org.springframework.test.context.cache - Spring test ApplicationContext cache statistics 日志,我发现有 2 个上下文缓存了预期的内容,因为两个 Specifications

上的注释不同

当我在 AbstractResourceProcessorSpec 的基础上将 @DirtiesContext 添加到 Specification 并连续重新执行测试时,它起作用了。

ApplicationContext 缓存在 DefaultCacheAwareContextLoaderDelegate 中的 ContextCache 中肯定是有什么事情发生了。或者它是别的东西,但我不知道它可能是什么。

有人知道吗?

已添加: 我比较了 Specifications 和 JmsSimulatedBusinessFailureSpec 中的 beans 还有 2 个额外的 beans:org.springframework.boot.test.context.ImportsContextCustomizer$ImportsCleanupPostProcessorFailureSimulator

已添加示例: https://github.com/pmihalcin/weird-spring-boot-test-behavior

尝试为您的 EvaluationHandler 使用 @MockBean 而不是使用方面来模拟系统故障。

来自 Gitter 频道的 Andy Wilkinson 的回答:

您的两个测试使用不同的配置,因此您有两个应用程序上下文 运行。 Spring 框架在 JVM 的生命周期内缓存上下文(保留它们 运行)以便它们可以被重用。结果,当第二个测试运行时,您有两个消息侦听器容器(每个上下文一个)侦听同一个队列。第一次测试的听众获胜。它具有简单的未经建议的 EvaluationHandler,因此不会发生模拟错误。将 @DirtiesContext 添加到第一个测试会阻止它被缓存,因此测试框架会在测试完成时关闭上下文。然后,这允许第二个测试的侦听器检索消息,然后由建议的处理程序处理并发生模拟错误。

推荐: 您可以使用 @DirtiesContext 或者您可以在模拟故障案例中自定义一些东西,以便它使用不同的队列? 或者您可以在 @BeforeClass@AfterClass 中启动和停止侦听器容器,同时保持上下文缓存