601

Is there any way, using Mockito, to mock some methods in a class, but not others?

For example, in this (admittedly contrived) Stock class I want to mock the getPrice() and getQuantity() return values (as shown in the test snippet below) but I want the getValue() to perform the multiplication as coded 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();
  }

  @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);
}
Ihor Patsian
  • 1,288
  • 2
  • 15
  • 25
Victor Grazi
  • 15,563
  • 14
  • 61
  • 94
  • 4
    Why would you want to do that? You should either be testing the class (in which case, there should not be mocking at all) or you should mock it while testing a different class (in which case, no functionality). Why would you do a partial mock? – weltraumpirat Feb 20 '13 at 01:01
  • 4
    Ok, this is a small example of the real thing. In reality, I am trying to avoid a call to the database, by passing in contrived values, but I want to verify that the other methods work correctly with those contrived values. Is there a better way to do this? – Victor Grazi Feb 20 '13 at 01:12
  • 6
    Certainly: Move your database calls to a separate class (domain logic and database access should not be in the same class; they are two different concerns), extract its interface, use that interface to connect from the domain logic class, and mock only the interface during testing. – weltraumpirat Feb 20 '13 at 01:15
  • 1
    I completely agree, it's hard to explain the whole picture without uploading gobs of code here, including third party libraries. – Victor Grazi Feb 20 '13 at 02:00
  • @weltraumpirat why the need for an interface? Couldn't you just create a StockDatabaseCommunicator class and define business logic there without the need to create an interface? – committedandroider Aug 04 '16 at 16:36
  • 1
    You probably could. But then, that wouldn't be "a better way to do it": Your database code is an implementation detail that you want to hide from the rest of your application, probably even move to a different package. You wouldn't want to have to recompile your domain logic every time you change a sequel statement, would you? – weltraumpirat Aug 16 '16 at 11:17
  • 1
    @weltraumpirat Why shouldn't we mock some methods of the very class under test? It certainly is possible that I'm testing one method of the my class and that method calls a different method of the same class. What about this causes a useless unit test? – Nikhil Vandanapu Jan 23 '20 at 00:03
  • As a rule of thumb, mocks should be used at system boundaries. If you're mocking functionality within your class under test, chances are, your test is tightly coupled to the implementation and will break more easily. And it's not going to help keeping your setup code readable. Is the test useless? Maybe not entirely. But I would still assume there's something not quite right with the design. – weltraumpirat Feb 03 '20 at 00:32
  • 1
    'Why would you want to do that?' Don't forget that you may have to work with a class that hasn't been well designed to be decomposable for mocking etc. Quite often I'm faced with classes that require me to jump through such hoops to permit testing. Only then can I refactor with some confidence – Brian Agnew Apr 08 '20 at 10:54

5 Answers5

954

To directly answer your question, yes, you can mock some methods without mocking others. This is called a partial mock. See the Mockito documentation on partial mocks for more information.

For your example, you can do something like the following, in your test:

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

In that case, each method implementation is mocked, unless specify thenCallRealMethod() in the when(..) clause.

There is also a possibility the other way around with spy instead of mock:

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

In that case, all method implementation are the real one, except if you have defined a mocked behaviour with when(..).

There is one important pitfall when you use when(Object) with spy like in the previous example. The real method will be called (because stock.getPrice() is evaluated before when(..) at runtime). This can be a problem if your method contains logic that should not be called. You can write the previous example like this:

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

Another possibility may be to use org.mockito.Mockito.CALLS_REAL_METHODS, such as:

Stock MOCK_STOCK = Mockito.mock( Stock.class, CALLS_REAL_METHODS );

This delegates unstubbed calls to real implementations.


However, with your example, I believe it will still fail, since the implementation of getValue() relies on quantity and price, rather than getQuantity() and getPrice(), which is what you've mocked.

Another possibility is to avoid mocks altogether:

@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);
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Jon Newmuis
  • 25,722
  • 2
  • 45
  • 57
  • Again, this was an oversimplification of the actual problem (see my response above). But I corrected my typo above, thanks for pointing it out. – Victor Grazi Feb 20 '13 at 01:13
  • 42
    I think this answer is wrong. You need to SPY an instance of the object, not to MOCK the class. – GaRRaPeTa Dec 08 '14 at 11:52
  • 4
    @GaRRaPeTa I would say spying and mocking are both reasonable alternatives. It's difficult to say which is the best for this case, since the OP states that this is a simplified example. – Jon Newmuis Dec 12 '14 at 04:54
  • 1
    SHouldn't it be "Spy" instead of "Mock" as partial mocking cabe be provided by "Spy" in a better way. – Tarun Sapra Jan 19 '16 at 14:42
  • 3
    `public static T spy(java.lang.Class classToSpy)` still has the annotation `@Incubating` as I write in September 2016... if you really want to use partial mocking the `spy` method should be called on an **instance** – mike rodent Sep 19 '16 at 18:39
  • How can I mock two method calls like stock.getMethod1().getMethod2()? – powder366 May 02 '17 at 13:57
  • 5
    `Stock stock = spy(Stock.class);` This seems wrong, `spy` method seems to accept only objects not classes. – Paramvir Singh Karwal Feb 06 '18 at 15:08
  • Is it possible to mock a method of a spied class which is called outside the test class? I need to use a real method but mock a call to other method inside the real one. – khodayar J Feb 14 '18 at 14:00
  • 2
    The answer is absolutely correct. The arguments in spy method are varied due to the jar. mockito-core-1.10.19 has two overload methods each for Class Type and Object but mockito-all-1.9.5 has only one that is for Object. I hope it would help someone to understand APIs. thank you! – Jugal Panchal Aug 14 '18 at 21:54
  • Looks like `@InjectMocks` does not understand the difference between `@Spy` and `@Mock` though so you end up having all methods stubbed even for spied classes? – Ghilteras Nov 10 '18 at 23:47
  • 16
    +1 for pointing out the difference between `doReturn(retval).when(spyObj).methodName(args)` and `when(spyObj.methodName(args)).thenReturn(retval)` – Captain_Obvious Nov 12 '18 at 23:44
  • Hi, Spy return generic and so Stock stock = spy(Stock.class) doesnt work. It shows need to have Class stock = spy(Stock.class) – pgman May 29 '19 at 15:04
  • @GaRRaPeTa it depends, there are rare cases where you may need to mock the entire class and only call for the real implementation of one method. – htafoya Mar 23 '20 at 23:15
  • Your `assertEquals` seem to be wrong – klutt Feb 24 '22 at 14:04
174

Partial mocking of a class is also supported via Spy in 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());

Check the 1.10.19 and 2.7.22 docs for detailed explanation.

Abdull
  • 26,371
  • 26
  • 130
  • 172
Sudarshan
  • 8,574
  • 11
  • 52
  • 74
45

According to docs :

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();
ema
  • 891
  • 11
  • 21
  • 2
    Thank you for demonstrating how to setup a mock where the real implementation is called for all methods except the few I need to control from the test. – bigh_29 Dec 03 '15 at 21:17
  • `class NaughtyLinkedList extends LinkedList { public int size() { throw new RuntimeException("don't call me");} } @Test public void partialMockNaughtLinkedList(){ List mock = mock(NaughtyLinkedList.class, CALLS_REAL_METHODS); mock.add(new Object()); // this calls the real function when(mock.size()).thenReturn(2); // For whatever reason, this lines throws the RuntimeException. assertEquals(2,mock.size()); }` This doesn't work. For whatever the reason, when "when" is executed, it actually executes the method that's supposed to be mocked. Code: – Lance Kind Feb 23 '16 at 06:31
  • 6
    The problem is "when." The "when" will actually execute the thing you want to partially mock. To avoid this there is an alternative: doReturn(). See the doReturn() at http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/Mockito.html#spy – Lance Kind Feb 23 '16 at 06:54
  • 2
    Tried to implement this answer and found that `when(mock.getSomething()).thenReturn(fakeValue);` does not work, rather had to use `doReturn(fakeValue).when(mock).getSomething()` – AdityaKapreShrewsburyBoston Feb 12 '22 at 02:27
20

What you want is org.mockito.Mockito.CALLS_REAL_METHODS according to the docs:

/**
 * 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>
 */

Thus your code should look like:

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

The call to Stock stock = mock(Stock.class); calls org.mockito.Mockito.mock(Class<T>) which looks like this:

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

The docs of the value RETURNS_DEFAULTS tell:

/**
 * 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.)
 */
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
the dude
  • 799
  • 9
  • 26
  • 1
    Well spotted... but can I just ask why you use `withSettings()...` like that? It appears that `org.mockito.internal.stubbing.answers.CallsRealMethods()` (for example) might do the job... and the javadoc for this class specifically says it's for use for partial mocks... – mike rodent Sep 19 '16 at 18:22
  • 3
    Also... won't this run into the problem encountered by other answers here: i.e. `thenReturn` will actually execute the method (which might cause problems, although not in this example), and so `doReturn` is preferable in such a case...? – mike rodent Sep 19 '16 at 18:32
3

Partial mocking using Mockito's spy method could be the solution to your problem, as already stated in the answers above. To some degree I agree that, for your concrete use case, it may be more appropriate to mock the DB lookup. From my experience this is not always possible - at least not without other workarounds - that I would consider as being very cumbersome or at least fragile. Note, that partial mocking does not work with ally versions of Mockito. You have use at least 1.8.0.

I would have just written a simple comment for the original question instead of posting this answer, but StackOverflow does not allow this.

Just one more thing: I really cannot understand that many times a question is being asked here gets comment with "Why you want to do this" without at least trying to understand the problem. Escpecially when it comes to then need for partial mocking there are really a lot of use cases that I could imagine where it would be useful. That's why the guys from Mockito provided that functionality. This feature should of course not be overused. But when we talk about test case setups that otherwise could not be established in a very complicated way, spying should be used.

kflGalore
  • 59
  • 1
  • 3
    I feel this answer is partially an opinion. Please consider editing. – soundslikeodd Jan 24 '17 at 21:31
  • 2
    Upvoted to cheer up the new member in the family. No need to get this in -ve zone, nothing really technically wrong there or incorrect language/tone. Be kind on new members.Thanks. – Saurabh Patil Dec 18 '18 at 07:11