使用Mockito嘲笑一些方法,但不是其他人

有没有什么方法可以用Mockito来嘲笑课堂上的某些方法,而不是其他的方法?

例如,在这个(诚然是人为devise)的Stock类中,我想模拟getPrice()和getQuantity()返回值(如下面的testing代码片段所示),但是我想让getValue()执行在Stock类

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文档 。

举个例子,你可以在你的testing中做如下的事情:

 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() ,否则每个方法实现都是thenCallRealMethod()

另一种方式也可能是间谍而不是模拟

 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(..)定义了一个模拟的行为。

当你像前面的例子那样使用间谍when(Object)时候有一个重要的陷阱。 真正的方法将被调用(因为stock.getPrice() when(..)在运行when(..)之前被计算)。 这可能是一个问题,如果你的方法包含不应该被调用的逻辑。 你可以像这样写前面的例子:

 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 

然而,就你的例子而言,我相信它仍然会失败,因为getValue()的实现依赖于quantityprice ,而不是getQuantity()getPrice() ,这就是你所嘲笑的。

它看起来像你想要的只是:

 @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); } 

部分mocking类也支持通过mockito 间谍

 List list = new LinkedList(); List spy = spy(list); //optionally, you can stub out some methods: when(spy.size()).thenReturn(100); //using the spy calls real methods spy.add("one"); spy.add("two"); //size() method was stubbed - 100 is printed System.out.println(spy.size()); 

详细解释请查看1.10.192.7.22文档。

根据文档 :

 Foo mock = mock(Foo.class, CALLS_REAL_METHODS); // this calls the real implementation of Foo.getSomething() value = mock.getSomething(); when(mock.getSomething()).thenReturn(fakeValue); // now fakeValue is returned value = mock.getSomething(); 

根据这个问题,接受的答案是不相符的。

Stock stock = mock(Stock.class); 调用org.mockito.Mockito.mock(Class<T>) ,如下所示:

  public static <T> T mock(Class<T> classToMock) { return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS)); } 

值为RETURNS_DEFAULTS的文档告诉:

 /** * The default <code>Answer</code> of every mock <b>if</b> the mock was not stubbed. * Typically it just returns some empty value. * <p> * {@link Answer} can be used to define the return values of unstubbed invocations. * <p> * This implementation first tries the global configuration. * If there is no global configuration then it uses {@link ReturnsEmptyValues} (returns zeros, empty collections, nulls, etc.) */ 

你想要的是根据文档org.mockito.Mockito.CALLS_REAL_METHODS

 /** * Optional <code>Answer</code> to be used with {@link Mockito#mock(Class, Answer)} * <p> * {@link Answer} can be used to define the return values of unstubbed invocations. * <p> * This implementation can be helpful when working with legacy code. * When this implementation is used, unstubbed methods will delegate to the real implementation. * This is a way to create a partial mock object that calls real methods by default. * <p> * As usual you are going to read <b>the partial mock warning</b>: * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects. * How does partial mock fit into this paradigm? Well, it just doesn't... * Partial mock usually means that the complexity has been moved to a different method on the same object. * In most cases, this is not the way you want to design your application. * <p> * However, there are rare cases when partial mocks come handy: * dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.) * However, I wouldn't use partial mocks for new, test-driven & well-designed code. * <p> * Example: * <pre class="code"><code class="java"> * Foo mock = mock(Foo.class, CALLS_REAL_METHODS); * * // this calls the real implementation of Foo.getSomething() * value = mock.getSomething(); * * when(mock.getSomething()).thenReturn(fakeValue); * * // now fakeValue is returned * value = mock.getSomething(); * </code></pre> */ 

因此你的代码应该如下所示:

 import org.junit.Test; import static org.mockito.Mockito.*; import static org.junit.Assert.*; public class StockTest { 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, withSettings().defaultAnswer(CALLS_REAL_METHODS)); when(stock.getPrice()).thenReturn(100.00); when(stock.getQuantity()).thenReturn(200); double value = stock.getValue(); assertEquals("Stock value not correct", 100.00 * 200, value, .00001); } } 

使用Mockito的间谍方法进行部分模拟可能是解决您的问题的方法,正如上面的答案中所述。 在某种程度上,我同意,对于具体的用例,嘲笑DB查找可能更合适。 从我的经验来看,这并不总是可行的 – 至less不是没有其他的解决方法 – 我会认为是非常麻烦或至less是脆弱的。 请注意,部分模拟不适用于盟友版本的Mockito。 你至less使用1.8.0。

我只是写了一个简单的原始问题的意见,而不是张贴这个答案,但StackOverflow不允许这样做。

还有一件事:我真的不明白,在这里问一个问题的次数很多时候会得到“你为什么要这么做”的评论,而至less不去理解这个问题。 特别是当涉及到需要部分模拟的时候,真的有很多用例可以想象它会在哪里有用。 这就是为什么Mockito的人提供了这个function。 这个function当然不会被过度使用。 但是,当我们谈论testing用例设置,否则不能以非常复杂的方式build立,应该使用间谍。