Is it possible, using Mockito, to create a partial mock where some methods are mocked but others retain their original behavior?
For instance, in the following Stock class, I want to mock the getPrice()
and getQuantity()
return values, but I want the getValue() method to execute the multiplication as defined 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();
}
}
In the following test code, I attempt to mock the getPrice()
and getQuantity()
methods but find that getValue() does not perform the calculation as expected:
@Test
public void getValueTest() {
Stock stock = mock(Stock.class);
when(stock.getPrice()).thenReturn(100.00);
when(stock.getQuantity()).thenReturn(200);
double value = stock.getValue();
// The assertion fails because the mock Stock getValue() method does not use the Stock.getValue() calculation code.
assertEquals("Stock value not correct", 100.00 * 200, value, .00001);
}
Use @Spy for Partial Mocks
You can use Mockito’s @Spy annotation to create a partial mock. This allows you to mock specific methods while keeping the original behavior of other methods.
Here’s how you can apply it:
import static org.mockito.Mockito.*;
public class StockTest {
@Spy
private Stock stock;
@Test
public void getValueTest() {
// Mock the getPrice() and getQuantity() methods
doReturn(100.00).when(stock).getPrice();
doReturn(200).when(stock).getQuantity();
double value = stock.getValue();
// Assert the value using the real getValue() calculation
assertEquals("Stock value not correct", 100.00 * 200, value, .00001);
}
}
Use a Custom Mock with Delegation
If you prefer to create a mock with specific behavior but without using @Spy, you can use Mockito’s Answer to delegate method calls to a real instance:
import static org.mockito.Mockito.*;
public class StockTest {
@Test
public void getValueTest() {
// Create a real instance
Stock realStock = new Stock(100.00, 200);
// Create a mock and use an Answer to delegate method calls
Stock mockStock = mock(Stock.class, new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
// Delegate calls to the real instance
return invocation.getMethod().invoke(realStock, invocation.getArguments());
}
});
// Mock the getPrice() and getQuantity() methods
when(mockStock.getPrice()).thenReturn(100.00);
when(mockStock.getQuantity()).thenReturn(200);
double value = mockStock.getValue();
// Assert the value using the real getValue() calculation
assertEquals("Stock value not correct", 100.00 * 200, value, .00001);
}
}