使用Mockito来模拟一些方法而不是其他方法

Vic*_*azi 354 java mocking mockito

有没有办法,使用Mockito来模拟一个类中的某些方法,而不是其他方法?

例如,在这个(公认的设计)Stock类中我想模拟getPrice()和getQuantity()返回值(如下面的测试片段所示),但我希望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);
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*uis 558

要直接回答你的问题,是的,你可以模拟一些方法而不会嘲笑其他方法.这称为部分模拟.有关详细信息,请参阅有关部分模拟的Mockito文档.

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

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
Run Code Online (Sandbox Code Playgroud)

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

还有一种可能性与间谍相反而不是模拟:

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
Run Code Online (Sandbox Code Playgroud)

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

when(Object)与前一个例子中一样使用间谍时,有一个重要的陷阱.将调用真实方法(因为在运行stock.getPrice()之前进行评估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
Run Code Online (Sandbox Code Playgroud)

然而,随着你的榜样,我相信它仍然会失败,因为实施org.mockito.Mockito.CALLS_REAL_METHODS依赖于getValue()quantity,而不是pricegetQuantity(),这是你嘲笑什么.

你真正想要的只是:

Stock MOCK_STOCK = Mockito.mock( Stock.class, CALLS_REAL_METHODS );
Run Code Online (Sandbox Code Playgroud)

  • 我认为这个答案是错误的.你需要SPY一个对象的实例,而不是MOCK该类. (16认同)
  • +1 指出 `doReturn(retval).when(spyObj).methodName(args)` 和 `when(spyObj.methodName(args)).thenReturn(retval)` 之间的区别 (11认同)
  • `Stock stock = spy(Stock.class);` 这似乎是错误的,`spy` 方法似乎只接受对象而不接受类。 (4认同)
  • @GaRRaPeTa我会说间谍和嘲笑都是合理的选择.由于OP声明这是一个简化的例子,因此很难说哪种情况最适合这种情况. (2认同)
  • `public static <T> T spy(java.lang.Class<T> classToSpy)` 仍然有我在 2016 年 9 月写的注释 `@Incubating`...如果你真的想使用部分模拟 `spy` 方法应该在**实例**上调用 (2认同)
  • 答案是绝对正确的。由于 jar 的不同,spy 方法中的参数也有所不同。mockito-core-1.10.19 有两种重载方法,分别用于类类型和对象,但mockito-all-1.9.5 只有一种用于对象。我希望它能帮助人们理解 API。谢谢你! (2认同)

Sud*_*han 129

在mockito中也可以通过Spy支持对类的部分模拟

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());
Run Code Online (Sandbox Code Playgroud)

查看1.10.192.7.22文档以获取详细说明.


ema*_*ema 33

根据文件:

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();
Run Code Online (Sandbox Code Playgroud)

  • 问题是"何时"."when"实际上会执行你想要部分模拟的东西.为了避免这种情况,有另一种选择:doReturn().请参阅http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/Mockito.html#spy上的doReturn() (3认同)
  • 感谢您演示如何设置一个模拟,在该模拟中,除我需要从测试中控制的几种方法外,所有方法都需要调用真正的实现。 (2认同)
  • 尝试实现这个答案,发现 `when(mock.getSomething()).thenReturn(fakeValue);` 不起作用,而不得不使用 `doReturn(fakeValue).when(mock).getSomething()` (2认同)

the*_*ude 17

根据问题,接受的答案是不正确的.

要将呼叫org.mockito.Mockito.CALLS_REAL_METHODS通话Stock stock = mock(Stock.class);,看起来像这样:

/**
 * 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>
 */
Run Code Online (Sandbox Code Playgroud)

值的文档org.mockito.Mockito.mock(Class<T>)告诉:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

你想要的是RETURNS_DEFAULTS根据文档:

 public static <T> T mock(Class<T> classToMock) {
    return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS));
}
Run Code Online (Sandbox Code Playgroud)

因此,您的代码应如下所示:

/**
 * 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.)
 */
Run Code Online (Sandbox Code Playgroud)

  • 另外......这不会遇到其他答案遇到的问题:即`thenReturn`实际上会执行该方法(这可能会导致问题,虽然不是在这个例子中),所以`doReturn`更适合这样的案件...? (3认同)