使用 Mockito 来模拟某些方法,但不能模拟其他方法

2022-01-08 00:00:00 mocking mockito java

有什么方法可以使用 Mockito 来模拟类中的某些方法,而不模拟其他方法?

Is there any way, using Mockito, to mock some methods in a class, but not others?

例如,在这个(诚然做作的)Stock 类中,我想模拟 getPrice()getQuantity() 返回值(如下面的测试片段所示),但我希望 getValue() 执行在 Stock 类中编码的乘法

For example, in this (admittedly contrived) Stock class I want to mock the getPrice() and getQuantity() return values (as shown in the test snippet below) but I want the getValue() to perform the multiplication as coded in the Stock class

public class Stock {
  private final double price;
  private final int quantity;

  Stock(double price, int quantity) {
    this.price = price;
    this.quantity = quantity;
  }

  public double getPrice() {
    return price;
  }

  public int getQuantity() {
    return quantity;
  }
  public double getValue() {
    return getPrice() * getQuantity();
  }

  @Test
  public void getValueTest() {
    Stock stock = mock(Stock.class);
    when(stock.getPrice()).thenReturn(100.00);
    when(stock.getQuantity()).thenReturn(200);
    double value = stock.getValue();
    // Unfortunately the following assert fails, because the mock Stock getValue() method does not perform the Stock.getValue() calculation code.
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}

推荐答案

直接回答你的问题,是的,你可以模拟一些方法而不模拟其他方法.这称为部分模拟.请参阅 关于部分模拟的 Mockito 文档了解更多信息信息.

To directly answer your question, yes, you can mock some methods without mocking others. This is called a partial mock. See the Mockito documentation on partial mocks for more information.

对于您的示例,您可以在测试中执行以下操作:

For your example, you can do something like the following, in your test:

Stock stock = mock(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
when(stock.getValue()).thenCallRealMethod();  // Real implementation

在这种情况下,每个方法实现都会被模拟,除非在 when(..) 子句中指定 thenCallRealMethod().

In that case, each method implementation is mocked, unless specify thenCallRealMethod() in the when(..) clause.

也有可能使用 spy 而不是 mock:

There is also a possibility the other way around with spy instead of mock:

Stock stock = spy(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
// All other method call will use the real implementations

在这种情况下,所有方法实现都是真实的,除非您使用 when(..) 定义了模拟行为.

In that case, all method implementation are the real one, except if you have defined a mocked behaviour with when(..).

当您将 when(Object) 与上一个示例一样与 spy 一起使用时,有一个重要的缺陷.真正的方法将被调用(因为 stock.getPrice() 在运行时在 when(..) 之前被评估).如果您的方法包含不应调用的逻辑,这可能是一个问题.你可以这样写前面的例子:

There is one important pitfall when you use when(Object) with spy like in the previous example. The real method will be called (because stock.getPrice() is evaluated before when(..) at runtime). This can be a problem if your method contains logic that should not be called. You can write the previous example like this:

Stock stock = spy(Stock.class);
doReturn(100.00).when(stock).getPrice();    // Mock implementation
doReturn(200).when(stock).getQuantity();    // Mock implementation
// All other method call will use the real implementations

<小时>

另一种可能是使用org.mockito.Mockito.CALLS_REAL_METHODS,如:

Stock MOCK_STOCK = Mockito.mock( Stock.class, CALLS_REAL_METHODS );

这将未存根的调用委托给实际实现.

This delegates unstubbed calls to real implementations.

但是,对于您的示例,我相信它仍然会失败,因为 getValue() 的实现依赖于 quantityprice,而不是 getQuantity()getPrice(),这是你嘲笑的.

However, with your example, I believe it will still fail, since the implementation of getValue() relies on quantity and price, rather than getQuantity() and getPrice(), which is what you've mocked.

另一种可能性是完全避免模拟:

Another possibility is to avoid mocks altogether:

@Test
public void getValueTest() {
    Stock stock = new Stock(100.00, 200);
    double value = stock.getValue();
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}

相关文章