Mockito:如何取消模拟一个方法?

59

我有一个包含不同测试方法的JUnit类。

我使用Mockito创建了一个真实实例的spy,并覆盖了一些与我执行的实际测试无关的方法。

是否有一种方法,仅仅为了在我的测试之后进行清理(以防其他测试也使用相同的实例并可能执行他们没有要求模拟的模拟方法),来取消模拟一个方法?

假设我有一个名为'wareHouseSpy'的spy对象。

假设我覆盖了方��isSomethingMissing

doReturn(false).when(wareHouseSpy).isSomethingMissing()

如何正确取消方法重写并将事物恢复到正常状态,使得下一次调用isSomethingMissing时能够运行真实的方法?

类似以下方式:

doReturn(Mockito.RETURN_REAL_METHOD).when(wareHouseSpy).isSomethingSpy()

或者可能是

Mockito.unmock(wareHouseSpy)

谁知道呢?我在那个领域找不到任何东西。

谢谢!

Assaf


1
我使用Mockito创建一个真实实例的spy,然后覆盖一些与我执行的实际测试无关的方法。我不理解,那你为什么要模拟它呢? - Liviu T.
你的例子中的 wareHouse 是单例模式还是其他什么?这些“其他测试”是在同一个类中还是你在谈论完全不同的一组测试? - jhericks
7个回答

63

我认为

Mockito.reset(wareHouseSpy)

会做它。


11
这将重置所有模拟方法。我认为原帖作者想要重置一个特定的方法(至少这就是我来这里查找的原因 :))。 - Hari Menon
我想重置该方法,然后使用新值重新存根该方法。但仍要保留该方法存根的 verify(..., times(1)) - tom_mai78101

23

假设你的大多数测试使用了存根响应。那么你将拥有一个类似于这样的setUp()方法:

@Before
public void setUp() {
  wareHouseSpy = spy(realWarehouse);
  doReturn(false).when(wareHouseSpy).isSomethingMissing();
}
现在假设您想在一个测试中撤销存根响应并使用真正的实现,请执行以下操作:
@Test
public void isSomethingMissing_useRealImplementation() {
  // Setup
  when(wareHouseSpy.isSomethingMissing()).thenCallRealMethod();

  // Test - Uses real implementation
  boolean result = wareHouseSpy.isSomethingMissing();
}

2
.thenCallRealMethod() 帮助我重置单个模拟方法。 - Rupesh
@Rupesh 做得很好。 - Jerry Brady

11

这取决于您是使用TestNG还是JUnit进行测试。

  • JUnit为每个测试方法创建一个新的实例。基本上您不必担心重置模拟对象。
  • 在使用TestNG时,您需要在@BeforeMethod@AfterMethod中通过 Mockito.reset(mockA, mockB, ...) 来重置模拟对象。

JUnit每个方法都创建一个新实例的说法不正确。我有一个方法,即使为每个方法执行所有模拟操作,它仍会干扰另一个方法的操作。 - AgilePro
1
@AgilePro 在2011年,只有JUnit 4,并且它的FAQ提到“它在测试类的每个测试中执行一个单独的实例”。其他人也写了同样的主题:测试类的生命周期。而这也是JUnit 5的默认设置,请参见测试实例生命周期:“…为了在隔离环境中执行…,JUnit在执行每个测试方法之前创建每个测试类的新实例”。 - bric3

4
“正常”的做法是在“setUp”方法中重新实例化对象。然而,如果你有一个真正的对象由于某些原因需要昂贵的构造,你可以这样做:
public class MyTests {

  private static MyBigWarehouse realWarehouse = new MyBigWarehouse();
  private MyBigWarehouse warehouseSpy;

  @Before
  public void setUp() {
    warehouseSpy = spy(realWarehouse); // same real object - brand new spy!
    doReturn(false).when(wareHouseSpy).isSomethingMissing();
  }

  @Test
  ...

  @Test
  ...

  @Test
  ...
}

除非您将realWarehouse声明为静态变量,否则它将为每个测试重新创建。 - Lucas Kuhlemann
你是对的。我修改了答案以反映这一点。 - jhericks

3
也许我没有理解清楚,但当你拥有一个真实的对象 real 时:
Object mySpy = spy(real);

然后要 "unspy" mySpy ... 只需使用real

2
根据文档,我们需要重置mocks。
reset(mock);
//at this point the mock forgot any interactions & stubbing

文档进一步说明:

通常情况下,您不需要重置模拟对象,只需为每个测试方法创建新的模拟对象即可。请考虑编写简单、小型和专注于测试方法的测试,而不是使用过长、过度规定的测试来代替 #reset()。

这里有一个他们的Github仓库中的示例,用于测试此行为并使用它:

@Test
public void shouldRemoveAllInteractions() throws Exception {
        mock.simpleMethod(1);
        reset(mock);
        verifyZeroInteractions(mock);
}

reference : ResetTest.java


0

针对这个特定的问题:

有没有一种方法,仅仅为了在我的测试之后清理,以防一些其他测试也使用相同的实例并可能执行一个他们没有请求模拟的模拟方法,来取消模拟方法?

如果你正在使用JUnit,最干净的方法是使用@Before@After(其他框架也有类似的)重新创建实例和 spy,以使没有测试依赖于或受到你在任何其他测试中所做的任何影响。然后,您可以在每个测试中进行特定于测试的 spy/mock 配置。如果出于某种原因您不希望重新创建对象,则可以重新创建 spy。不管哪种方式,每个人每次都从一个新鲜的 spy 开始。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接