首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Mockito Capture在捕获时没有维护捕获列表。

Mockito Capture在捕获时没有维护捕获列表。
EN

Stack Overflow用户
提问于 2017-10-25 14:05:48
回答 4查看 4.1K关注 0票数 7

在Mockito中,我们有一种情况,即列表的捕获不会返回预期的结果。测试用例:

  1. 我们在列表中添加"Pip“
  2. 我们抓住了名单
  3. 我们在名单上加上"Sok“。

在我们的断言中,我们只期望"Pip“在那里,而"Sok”也在那里。我们认为这是不正确的,因为在抓捕时,"Sok“不在名单上。

java.lang.AssertionError:

预期:Pip

实战:Pip,Sok

  • 有人找到解决办法了吗?
  • 这是Mockito的一个bug还是一个特性?
  • 为什么Mockito保留对列表的引用而不将列表的副本作为捕获时间?

下面是测试用例:

代码语言:javascript
运行
复制
@RunWith(MockitoJUnitRunner.class)
public class CaptureTest {

    @Captor
    private ArgumentCaptor<List> listCapture;

    @Mock
    private ListPrinter listPrinter;

    private TestClass testClass;

    @Before
    public void setUp() {
        testClass = new TestClass(listPrinter);
    }

    @Test
    public void testCapture() {
        testClass.simulateFailSituation();
        verify(listPrinter).printList(listCapture.capture());
        // THIS FAILS: Expected:[Pip],  Actual:[Pip, Sok]
        assertEquals(Collections.singletonList("Pip"), listCapture.getValue());
    }

    public class TestClass {

        private List list = new ArrayList();
        private ListPrinter listPrinter;

        public TestClass(ListPrinter listPrinter) {
            this.listPrinter = listPrinter;
        }

        private void simulateFailSituation() {
            list.add("Pip");
            listPrinter.printList(list);
            list.add("Sok");
        }
    }

    public interface ListPrinter {
        void printList(List list);
    }
  }
EN

回答 4

Stack Overflow用户

发布于 2017-10-25 16:13:47

这听起来可能是一个令人惊奇的功能,但你可以这样想:如果它在复制,它应该停在哪里?您可能会捕获一些对其他对象有许多引用的对象,最终可能会对JVM实例中的几乎所有对象进行深度复制。

这将是一个严重的表现,所以我有点理解的原因。

因此,您可以从以下两种方法中选择:

  • 在适用的情况下使用不可变的对象。除了易于测试之外,它还使代码更易于阅读和调试。
  • 在调用时立即测试该值,而不是捕获引用。对于void方法,您可以将doAnswer方法与Answer<Void>一起使用,在那里进行测试或复制。顺便说一句,这是新的,请参阅How to make mock to void methods with mockito

我发现它比核实要强大得多。在您的例子中,doAnswer看起来可能是这样的:

代码语言:javascript
运行
复制
doAnswer(invocation -> {
    assertEquals(Collections.singletonList("Pip"), invocation.getArguments()[0]);
    return null;
}).when(listPrinter).printList(Matchers.anyList());
票数 5
EN

Stack Overflow用户

发布于 2017-10-25 17:07:59

要同意Vlasec的回答,默认情况下,对Mockito进行深度复制是没有意义的。它无法判断哪些对象是不可变的值对象(如字符串),哪些对象易于复制(ArrayList?),哪些对象绝对不应该被复制(线程?),等等。但是,当方法被调用时,您可以使用一个答案来创建自己的副本。

代码语言:javascript
运行
复制
@Test
public void testCapture() {
    // from memory - may need warnings suppressed or different casts/generics
    List<String> listSnapshot = new ArrayList<>();
    doAnswer(invocation -> {
        listSnapshot.addAll((List) invocation.getArguments()[0]);
        return null;
    }).when(listPrinter).printList(any());
    testClass.simulateFailSituation();
    listCapture.capture());

    assertEquals(Collections.singletonList("Pip"), listSnapshot);
}

尽管Vlasec的更新显示了在一个答案中执行验证的方法,但我更喜欢手动捕获,因为它更接近ArgumentCaptor,更适合于“给定/何时/然后”或“期望/执行/验证”测试格式。此外,在测试系统处于堆栈中时,失败的断言中间测试将导致Mockito失败,这可能导致比测试方法返回后的验证更不明显的失败。

票数 2
EN

Stack Overflow用户

发布于 2017-10-25 16:41:37

处理这件事的一种方法是打电话

代码语言:javascript
运行
复制
      doAnswer(invocationOnMock -> {return null;}).when(listPrinter).printList(any());

然后在钩子做自己的捕获与深拷贝,或核实到位。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46934599

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档