基于 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.");
}
}
而 EvaluationHandler
是 org.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 个上下文缓存了预期的内容,因为两个 Specification
s
上的注释不同
当我在 AbstractResourceProcessorSpec
的基础上将 @DirtiesContext
添加到 Specification
并连续重新执行测试时,它起作用了。
ApplicationContext
缓存在 DefaultCacheAwareContextLoaderDelegate
中的 ContextCache
中肯定是有什么事情发生了。或者它是别的东西,但我不知道它可能是什么。
有人知道吗?
已添加:
我比较了 Specification
s 和 JmsSimulatedBusinessFailureSpec
中的 beans 还有 2 个额外的 beans:org.springframework.boot.test.context.ImportsContextCustomizer$ImportsCleanupPostProcessor
和 FailureSimulator
已添加示例:
https://github.com/pmihalcin/weird-spring-boot-test-behavior
尝试为您的 EvaluationHandler
使用 @MockBean
而不是使用方面来模拟系统故障。
来自 Gitter 频道的 Andy Wilkinson 的回答:
您的两个测试使用不同的配置,因此您有两个应用程序上下文 运行。 Spring 框架在 JVM 的生命周期内缓存上下文(保留它们 运行)以便它们可以被重用。结果,当第二个测试运行时,您有两个消息侦听器容器(每个上下文一个)侦听同一个队列。第一次测试的听众获胜。它具有简单的未经建议的 EvaluationHandler
,因此不会发生模拟错误。将 @DirtiesContext
添加到第一个测试会阻止它被缓存,因此测试框架会在测试完成时关闭上下文。然后,这允许第二个测试的侦听器检索消息,然后由建议的处理程序处理并发生模拟错误。
推荐:
您可以使用 @DirtiesContext
或者您可以在模拟故障案例中自定义一些东西,以便它使用不同的队列?
或者您可以在 @BeforeClass
和 @AfterClass
中启动和停止侦听器容器,同时保持上下文缓存
鉴于我的 Web 应用程序也有 Spring 包含 EvaluationHandler
的集成管道,我想模拟业务处理失败。我创建了以下方面:
@Aspect
class FailureSimulator {
@Before("execution(* *..*EvaluationHandler+.handle(..))")
static void fail() {
simulateBusinessProcessingFailure();
}
private static void simulateBusinessProcessingFailure() {
throw new DataIntegrityViolationException("Simulated failure.");
}
}
而 EvaluationHandler
是 org.springframework.integration.dsl.support.GenericHandler
我创建了导入 FailureSimulator
@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 个上下文缓存了预期的内容,因为两个 Specification
s
当我在 AbstractResourceProcessorSpec
的基础上将 @DirtiesContext
添加到 Specification
并连续重新执行测试时,它起作用了。
ApplicationContext
缓存在 DefaultCacheAwareContextLoaderDelegate
中的 ContextCache
中肯定是有什么事情发生了。或者它是别的东西,但我不知道它可能是什么。
有人知道吗?
已添加:
我比较了 Specification
s 和 JmsSimulatedBusinessFailureSpec
中的 beans 还有 2 个额外的 beans:org.springframework.boot.test.context.ImportsContextCustomizer$ImportsCleanupPostProcessor
和 FailureSimulator
已添加示例: https://github.com/pmihalcin/weird-spring-boot-test-behavior
尝试为您的 EvaluationHandler
使用 @MockBean
而不是使用方面来模拟系统故障。
来自 Gitter 频道的 Andy Wilkinson 的回答:
您的两个测试使用不同的配置,因此您有两个应用程序上下文 运行。 Spring 框架在 JVM 的生命周期内缓存上下文(保留它们 运行)以便它们可以被重用。结果,当第二个测试运行时,您有两个消息侦听器容器(每个上下文一个)侦听同一个队列。第一次测试的听众获胜。它具有简单的未经建议的 EvaluationHandler
,因此不会发生模拟错误。将 @DirtiesContext
添加到第一个测试会阻止它被缓存,因此测试框架会在测试完成时关闭上下文。然后,这允许第二个测试的侦听器检索消息,然后由建议的处理程序处理并发生模拟错误。
推荐:
您可以使用 @DirtiesContext
或者您可以在模拟故障案例中自定义一些东西,以便它使用不同的队列?
或者您可以在 @BeforeClass
和 @AfterClass
中启动和停止侦听器容器,同时保持上下文缓存