返回

如何让 Mockito 伪造对象返回不同的值?

java

Mockito 中伪造对象返回值的动态控制

前言

在 Java 单元测试中,Mockito 是一个流行的模拟框架,它允许我们创建伪造对象来替代实际对象,从而进行隔离测试。Mockito 的强大功能之一是使用 when()thenReturn() 方法模拟方法的返回值。然而,在某些情况下,你可能希望伪造对象在不同测试中返回不同的值。本文将深入探讨如何在不重新创建伪造对象的情况下实现这一目标。

问题

让我们考虑一个示例,其中一个伪造对象 mockFoo 在类级别作为一个静态变量创建。在第一个测试中,我们希望 mockFoo.someMethod() 返回 0,而在第二个测试中,我们希望它返回 1。

// 类级别伪造对象
private static Foo mockFoo;

@BeforeClass
public static void setUp() {
    mockFoo = mock(Foo.class);
}

// 测试 1
@Test
public void test1() {
    when(mockFoo.someMethod()).thenReturn(0);
    ...
}

// 测试 2
@Test
public void test2() {
    when(mockFoo.someMethod()).thenReturn(1);
    ...
}

在第二个测试中,我们注意到 mockFoo.someMethod() 仍然返回 0,尽管我们已经更新了 when() 语句以返回 1。这是因为 Mockito 在第一次调用 when() 方法后缓存了返回值。

解决方案

为了解决这个问题,有两种方法:

方法 1:使用 reset() 方法

在每个测试之前,可以使用 Mockito.reset(mockFoo) 方法重置伪造对象的状态。这将清除所有先前的模拟,让你可以重新定义返回值。

方法 2:使用 Answer 接口

Answer 接口提供了一个更灵活的方法来定义模拟方法的返回值。通过实现 Answer 接口,你可以创建一个自定义的回答对象,该对象决定在每次调用时返回的值。

mockFoo = mock(Foo.class, new Answer<Integer>() {
    private int count = 0;

    @Override
    public Integer answer(InvocationOnMock invocation) {
        return count++;
    }
});

在上面的示例中,我们创建了一个 Answer 对象,它维护一个计数器,用于在每次调用时返回不同的值。

结论

在 Mockito 中,有两种方法可以解决如何让伪造对象在下一次被调用时返回不同的值。根据具体情况,reset() 方法或 Answer 接口都可以提供有效的解决方案。通过理解这两种方法,你可以确保在测试中灵活地模拟对象行为。

常见问题解答

  1. 为什么 when() 方法会缓存返回值?

    • 缓存返回值有助于提高性能,因为它消除了在每次调用时重新计算返回值的需要。
  2. 什么时候应该使用 reset() 方法?

    • 当你希望在不同的测试中使用相同的伪造对象时,并且需要在每次测试之前重置它的状态时,应该使用 reset() 方法。
  3. 什么时候应该使用 Answer 接口?

    • 当你需要返回根据调用次数或其他因素动态计算的值时,应该使用 Answer 接口。
  4. 除了本文介绍的两种方法外,还有其他方法可以让伪造对象返回不同的值吗?

    • 没有其他内置的方法,但你可以使用其他技术,例如手动创建多个伪造对象或使用 spy 对象。
  5. 哪种方法更好,reset() 还是 Answer

    • 这取决于具体情况。reset() 方法更简单,而 Answer 接口更灵活。