【问题标题】:Spring Boot - @SQSListener and @SpyBean are not working together?Spring Boot - @SQSListener 和 @SpyBean 没有一起工作?
【发布时间】:2020-09-06 13:32:23
【问题描述】:

我有一个简单的带有 AWS SQS 集成的 Spring Boot 应用程序。 在集成测试中,我试图监视使用 @SqsListener 注释注释的方法,并得到一个 bean sometimes 没有被监视的行为。

您可以在这里查看示例项目:https://github.com/Sanych/aws-sqs-spybean-troubleshooting

运行mvn clean verify

在此日志中,人们可能会注意到,3 种测试方法中有 2 种被成功监视:

2020-09-06 15:39:23.597  INFO 18372 --- [           main] com.aws.test.demo.SQSListenerIT          : Started SQSListenerIT in 0.813 seconds (JVM running for 24.024)
2020-09-06 15:39:23.604  WARN 18372 --- [           main] com.aws.test.demo.SQSListenerIT          : calling class com.aws.test.demo.SQSListener$MockitoMock$1506819983
2020-09-06 15:39:23.604  WARN 18372 --- [           main] com.aws.test.demo.SQSListenerIT          : sendMessage to queue: test 1
2020-09-06 15:39:23.810  WARN 18372 --- [enerContainer-2] com.aws.test.demo.SQSListener            : id: class com.aws.test.demo.SQSListener$MockitoMock$1506819983, received: 'test 1'
2020-09-06 15:39:23.929  WARN 18372 --- [           main] com.aws.test.demo.SQSListenerIT          : calling class com.aws.test.demo.SQSListener$MockitoMock$1506819983
2020-09-06 15:39:23.929  WARN 18372 --- [           main] com.aws.test.demo.SQSListenerIT          : sendMessage to queue: test 2
2020-09-06 15:39:24.052  WARN 18372 --- [enerContainer-2] com.aws.test.demo.SQSListener            : id: class com.aws.test.demo.SQSListener, received: 'test 2'
2020-09-06 15:39:29.148  WARN 18372 --- [           main] com.aws.test.demo.SQSListenerIT          : calling class com.aws.test.demo.SQSListener$MockitoMock$1506819983
2020-09-06 15:39:29.148  WARN 18372 --- [           main] com.aws.test.demo.SQSListenerIT          : sendMessage to queue: test 3
2020-09-06 15:39:29.175  WARN 18372 --- [enerContainer-2] com.aws.test.demo.SQSListener            : id: class com.aws.test.demo.SQSListener$MockitoMock$1506819983, received: 'test 3'
[ERROR] Tests run: 3, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 6.468 s <<< FAILURE! - in com.aws.test.demo.SQSListenerIT
[ERROR] test2  Time elapsed: 5.218 s  <<< ERROR!
org.awaitility.core.ConditionTimeoutException:
Assertion condition defined as a lambda expression in com.aws.test.demo.SQSListenerIT
Wanted but not invoked:
SQSListener.listen(<any string>);
-> at com.aws.test.demo.SQSListenerIT.lambda$sendMessageFromFileAndWaitForListenerToRead$0(SQSListenerIT.java:57)
Actually, there were zero interactions with this mock.
 within 5 seconds.
        at com.aws.test.demo.SQSListenerIT.sendMessageFromFileAndWaitForListenerToRead(SQSListenerIT.java:57)
        at com.aws.test.demo.SQSListenerIT.test2(SQSListenerIT.java:42)
Caused by: org.mockito.exceptions.verification.WantedButNotInvoked:

Wanted but not invoked:
SQSListener.listen(<any string>);
-> at com.aws.test.demo.SQSListenerIT.lambda$sendMessageFromFileAndWaitForListenerToRead$0(SQSListenerIT.java:57)
Actually, there were zero interactions with this mock.
        at com.aws.test.demo.SQSListenerIT.lambda$sendMessageFromFileAndWaitForListenerToRead$0(SQSListenerIT.java:57)

我错过了什么吗?

【问题讨论】:

  • 我猜从你的日志来看,SQSListener 没有被触发。未调用意味着您等待一段时间但未调用。你能分享你的测试吗?
  • 另外,您不需要测试 sqs 侦听器,因为您正在测试注释。您可以编写单元测试,但它有点奇怪。对于每个 IT 测试,您都需要付出代价 :)
  • @Gurkanİlleez 代码示例位于 git repo github.com/Sanych/aws-sqs-spybean-troubleshooting
  • 我遇到了一个非常相似的问题。您是否设法找到任何解决此问题的方法?

标签: java spring-boot junit amazon-sqs


【解决方案1】:

遇到了完全相同的问题。

来自SpyBean文档:

同一类型上下文中的所有 bean 都将被 spy 包装。

经过彻底调查,对我来说唯一合理的解释是SqsListener方法/bean是在之前注册的MockitoPostProcessor实际上用间谍代理包装了现有的Bean。

因此,“真正的”bean 将被实际调用,但无法使用 verify(...) 验证此调用,因为该调用不是通过 Spy 代理触发的。

可能很高兴知道:@SqsListener 方法是通过 QueueMessageHandler 注册的,它是由 SqsAutoConfiguration 构造的。因此,如果 SqsAutoConfigurationMockitoPostProcessor 包装 spy bean 之前运行,则调用不会通过 Spy 代理。

可能的解决方案

我使用以下方法来解决这个问题..


public class MyService {

    @Autowired
    private OtherService otherService;

    @SqsListener("queue")
    public void listen(@NotificationMessage Event event) {
        oterService.doWork();
    }

}
  • 从集成测试(测试实际调用MyService#listen)拆分单元测试(功能测试MyService
  • 在集成测试中 (@SpringBootTest):不要 @SpyBean 监听器 bean 本身,而是监听器使用的 bean。 (在我的例子中是OtherService
  • verify(otherService, timeout(500)).doWork()

虽然这个答案比较抽象,但我希望这能有所帮助。

编辑: 仅仅使用 MockBean 模拟 OtherService 是不够的。我必须显式创建一个OtherService 模拟(mock(OtherService.class)并使用包私有设置器将其设置在我的MyService 中。 我承认,这不是最干净的解决方案,但到目前为止,这是唯一对我有用的解决方案。

编辑(2022-02-16): 从 Spring Cloud AWS 2.4 开始,@SqsTest 注释可用于此目的: https://github.com/awspring/spring-cloud-aws/releases/tag/v2.4.0 https://github.com/awspring/spring-cloud-aws/blob/v2.4.0/spring-cloud-aws-samples/spring-cloud-aws-sqs-sample/src/test/java/io/awspring/cloud/sqs/sample/SqsSampleApplicationTests.java

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-11
    • 2018-12-24
    • 2019-02-17
    • 2017-02-28
    • 1970-01-01
    • 2017-03-23
    • 1970-01-01
    相关资源
    最近更新 更多