使用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()
的实现依赖于quantity
和price
,而不是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.19
和2.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立,应该使用间谍。