当前位置: 首页 > 知识库问答 >
问题:

返回在嘲笑时错误执行的建议(在实际执行之前)

蔚和安
2023-03-14

在集成测试中,我的@afterreturn建议被错误执行,而在测试中我模拟它以抛出TimeoutException,并且传递给方面的参数为null。

我的建议是:

    @AfterReturning("execution(* xxxxxx" +
            "OrderKafkaProducerService.sendOrderPaidMessage(..)) && " +
            "args(order)")
    public void orderComplete(CheckoutOrder order) { // order is null when debugging
        metricService.orderPaidKafkaSent();
        log.trace("Counter inc: order paid kafka"); // this line of log is shown in console
        metricService.orderCompleted();
        log.trace("Order complete! {}", order.getId()); // this line is not, because NPE
    }

我的测试:

// mocking
doThrow(new ServiceException(new TimeoutException("A timeout occurred"), FAILED_PRODUCING_ORDER_MESSAGE))
    .when(orderKafkaProducerService).sendOrderPaidMessage(any()); // this is where advice is executed, which is wrong

...
// when
(API call with RestAssured, launch a real HTTP call to endpoint; service is called during this process)

// then
verify(orderKafkaProducerService).sendOrderPaidMessage(any(CheckoutOrder.class)); // it should be called
verify(metricService, never()).orderCompleted(); // but we are throwing, not returning, we should not see this advice executed

由于NPE(订单为空),此测试失败。

在调试中,我发现当我进行模拟时,我已经执行了建议,此时,any()还没有值,为空,所以NPE。但我不认为这些建议应该在嘲笑的时候执行。测试时如何避免这种情况??这对我来说太荒谬了。

共有1个答案

公西苗宣
2023-03-14

目前Spring测试支持没有显式处理注入的mock或间谍(这是通过Mockito的代理子类)可能实际上是稍后的AOP目标(即代理并因此通过CGLIB再次子类化)的情况。

关于Spring、Spring Boot和Mockito,有几个与此主题相关的错误通知单。还没有人对此采取任何行动。我确实理解为什么Mockito维护人员不将特定于Spring的内容包含到他们的代码库中,但我不理解为什么Spring人员不改进他们的测试工具。

实际上,当调试失败的测试并检查kafkaService时,您会发现以下事实:

  1. kafkaService.getClass()iscom.example.template.services.KafkaService$MockitoMock92961867$$EnhancerBySpringCGLIB$$8fc4fe95
  2. kafkaService.getClass(). getSuperclass()iscom.example.template.services.KafkaService$MockitoMock92961867$
  3. kafkaService.getClass(). getSuperclass(). getSuperclass()是类com.example.template.services.KafkaService

换句话说:

  1. kafkaService是一个CGLIB Spring AOP代理。
  2. AOP代理包装一个Mockito间谍(可能是ByteBuddy代理)。
  3. Mockito间谍包裹了原始物体。

此外,更改包装顺序以使Mockito spy成为最外层的对象将不起作用,因为CGLIB故意使其覆盖方法成为最终方法,即不能再次扩展和覆盖它们。如果Mockito也有同样的限制,那么分层包装就根本不起作用。

不管怎样,你能做什么?

  • 要么使用本教程中描述的复杂方法

在您的情况下,后一种解决方案如下所示:

@Test
void shouldCompleteHappyPath() {
    // fetch spy bean by unwrapping the AOP proxy, if any
    KafkaService kafkaServiceSpy = AopTestUtils.getTargetObject(kafkaService);
    // given mocked
    doNothing().when(kafkaServiceSpy).send(ArgumentMatchers.any());
    // when (real request)
    testClient.create().expectStatus().isOk();
    // then
    verify(kafkaServiceSpy).send(ArgumentMatchers.any());
    verify(metricService).incKafka();
}

这样做的效果是,当(kafkaServiceSpy)时。发送(ArgumentMatchers.any())不再触发方面建议,因为kafkaServiceSpy不再是AOP代理。不过,自动连接的beankafkaService仍然是,因此AOP如预期的那样被触发,但在记录模拟交互时不再不受欢迎地被触发。

实际上,对于验证,您甚至可以使用kafkaService来代替间谍,并且只有在记录您以后想要验证的交互时才打开间谍:

@Test
void shouldCompleteHappyPath() {
    // given mocked
    doNothing()
      .when(
        // fetch spy bean by unwrapping the AOP proxy, if any
        AopTestUtils.<KafkaService>getTargetObject(kafkaService)
      )
      .send(ArgumentMatchers.any());
    // when(real request)
    testClient.create().expectStatus().isOk();
    // then
    verify(kafkaService).send(ArgumentMatchers.any());
    verify(metricService).incKafka();
}

附言:如果没有你的MCVE,我将永远无法调试它,并找出到底发生了什么。这再次证明,提出包括MCVE在内的问题是你能为自己做的最好的事情,因为它可以帮助你获得问题的答案,否则这些问题可能仍然没有答案。

更新:在我在类似的闭题Spring Boot#6871中提到这个问题后,一位维护人员自己创建了Spring Boot#22281,专门解决了您的问题。你可能想看看新问题,看看是否/何时可以解决。

 类似资料:
  • 当我使用Maven执行mvn将文件复制到远程服务器时。 然而,我得到下面的错误信息。我该怎么解决这个问题? 我正在遵循一个例子,如帖子所示https://jarirajari.wordpress.com/2014/06/11/copy-files-and-execute-command-on-a-remote-host-with-maven-antrun-plugin-without-ant-us

  • 问题内容: 从终端以下作品没问题 其中包含一些文件(例如,file1,file2和file3)的目录在哪里。 但是,在Java中使用以下命令时出现错误 错误是 我相信我正确地转义了字符。我尝试了几种不同的格式,但无法正常使用。 UPDATE @jtahlborn完美地回答了这个问题。但是命令现在已经稍作更改,以在计算md5sum之前对目录中的每个文件进行排序,如下所示(我已经接受了原始问题的出色答

  • 我有一个视图,它具有激活viewpager的表格布局,当查看页面时,它工作正常,但如果我单击该视图上的某个项目,然后返回到过去的屏幕,我会得到: Java.Lang.IllegalStateException:FragmentManager已经在执行事务。 不确定这是否有所不同,但当我离开这个视图时,我从一个片段变成了一个活动。

  • 我看过几篇关于这个主题的帖子(post1,post2,post3)。在所有这些问题中,解决方案看起来都很容易,但我无法得到返回值的值。 这是我到目前为止所尝试的: 备选办法1: 备选案文3: 结果:原因,如果还注意到 创建到Java代码中的查询如下所示: 和连接字符串:

  • 问题内容: 堆栈跟踪: 引用此代码http://pastebin.com/B9V6yvFu 奇怪的是,在我的LG4X上它可以正常工作,但是在我的三星s2上它会抛出上述错误。有什么想法怎么了? 问题答案: 因为在现有的消化周期内进行调用,所以收到此错误。 最大的问题是:您为什么打电话?除非您是从非Angular事件进行接口的,否则您根本不需要调用。通常的存在意味着我做错了事(除非,再次,$ appl

  • 我面临这个运行时错误。它说: 错误:任务“:app:dexDebug”的执行失败。 com.android.ide.common.process.ProcessExcture:org.gradle.process.internal.ExecExc0019:进程'命令'/usr/lib/jvm/java-8-oracle/bin/java"完成非零退出值2 这是我的身材。渐变脚本: 请帮帮我。谢谢!