Use Mockito to mock some methods but not others
有没有办法,使用mockito,在一个类中模拟一些方法,而不是其他方法?
例如,在这个(公认是人为设计的)股票类中,我想模拟getprice()和getquantity()返回值(如下面的测试片段所示),但我希望getvalue()执行股票类中编码的乘法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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文档。
对于您的示例,您可以在测试中执行如下操作:
1 2 3 4 | 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 |
在这种情况下,每个方法实现都是模拟的,除非在
还有一种可能是用另一种方法对付间谍而不是嘲笑:
1 2 3 4 | 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 |
在这种情况下,所有方法实现都是真实的,除非您已经用
在使用
1 2 3 4 | 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 |
但是,以你的例子来说,我相信它仍然会失败,因为
你真正想要的是:
1 2 3 4 5 6 | @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); } |
在mockito中,通过间谍也支持对类的部分模拟。
1 2 3 4 5 6 7 8 9 10 11 12 | 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 2 3 4 5 6 7 8 9 | 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(); |
根据问题,接受的答案不正确。
对
1 2 3 | public static <T> T mock(Class<T> classToMock) { return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS)); } |
价值
1 2 3 4 5 6 7 8 9 10 11 | /** * The default <wyn>Answer</wyn> of every mock if 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.) */ |
根据文件,您需要的是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /** * Optional <wyn>Answer</wyn> 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 the partial mock warning: * 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: * [cc lang="java"]<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(); * |
*/< /代码>
因此,您的代码应该如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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的spy方法进行部分模拟可能是解决您问题的方法,如上面的答案中所述。在某种程度上,我同意,对于您的具体用例,模拟数据库查找可能更合适。根据我的经验,这并不总是可能的——至少没有其他的解决办法——我认为这是非常麻烦的,或者至少是脆弱的。注意,部分模拟不适用于盟友版本的mockito。您至少使用了1.8.0。
我只是为原始问题写了一个简单的评论,而不是发布这个答案,但是StackOverflow不允许这样做。
还有一件事:我真的不明白,很多时候这里都会有人问一个问题,"为什么你要这样做",但至少要试着去理解这个问题。特别是当需要进行部分模拟时,我可以想象到在哪里它会有用。这就是为什么Mockito的人提供了这个功能。当然,这个特性不应该被过度使用。但是,当我们谈论测试用例设置时,如果不是以一种非常复杂的方式建立的话,就应该使用间谍。