295

So, I'm creating a mock object as a static variable on the class level like so... In one test, I want Foo.someMethod() to return a certain value, while in another test, I want it to return a different value. The problem I'm having is that it seems I need to rebuild the mocks to get this to work correctly. I'd like to avoid rebuilding the mocks, and just use the same objects in each test.

class TestClass {

    private static Foo mockFoo;

    @BeforeClass
    public static void setUp() {
        mockFoo = mock(Foo.class);
    }

    @Test
    public void test1() {
        when(mockFoo.someMethod()).thenReturn(0);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), receiving 0 as the value

    }

    @Test
    public void test2() {
        when(mockFoo.someMethod()).thenReturn(1);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), STILL receiving 0 as the value, instead of expected 1.

    }

}

In the second test, I'm still receiving 0 as the value when testObj.bar() is called... What is the best way to resolve this? Note that I know I could use a different mock of Foo in each test, however, I have to chain multiple requests off of mockFoo, meaning I'd have to do the chaining in each test.

DigitalZebra
  • 39,494
  • 39
  • 114
  • 146

5 Answers5

549

You could also Stub Consecutive Calls (#10 in 2.8.9 api). In this case, you would use multiple thenReturn calls or one thenReturn call with multiple parameters (varargs).

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

public class TestClass {

    private Foo mockFoo;

    @Before
    public void setup() {
        setupFoo();
    }

    @Test
    public void testFoo() {
        TestObject testObj = new TestObject(mockFoo);

        assertEquals(0, testObj.bar());
        assertEquals(1, testObj.bar());
        assertEquals(-1, testObj.bar());
        assertEquals(-1, testObj.bar());
    }

    private void setupFoo() {
        mockFoo = mock(Foo.class);

        when(mockFoo.someMethod())
            .thenReturn(0)
            .thenReturn(1)
            .thenReturn(-1); //any subsequent call will return -1

        // Or a bit shorter with varargs:
        when(mockFoo.someMethod())
            .thenReturn(0, 1, -1); //any subsequent call will return -1
    }
}
Freek de Bruijn
  • 3,552
  • 2
  • 22
  • 28
Tony R
  • 7,136
  • 4
  • 21
  • 12
  • 195
    I think you can also take advantage of the fact that .thenReturn() takes varargs, so the code can be shortened to: when(mockFoo.someMethod()).thenReturn(0, 1, -1); – Justin Muller Jun 11 '12 at 04:25
  • 10
    @JustinMuller - that's worth a separate answer, I think (as opposed to a comment) – Brian Agnew Nov 11 '14 at 12:29
  • 21
    This answer is not the correct thing to do in this case. If you stub this method to return 0 and 1, then you'll be fine so long as you run `test1` and then `test2`. But it may be that your continuous integration environment will run the tests in the other order. Or it may be that you'll want to run `test2` by itself, without running `test1` first, in which case it will fail. Unit tests _must_ always be independent of each other; and there should _never_ be a dependency between individual tests, or a dependency on a particular ordering of tests. Whereas chaining `thenReturn` statements ... – Dawood ibn Kareem Jul 21 '15 at 09:18
  • 5
    ... has its uses, as does using varargs for a single `thenReturn`, it's not a correct solution in this particular case. It seems to me that the hordes of upvoters here have most likely failed to understand the question. – Dawood ibn Kareem Jul 21 '15 at 09:19
  • 2
    Junit itself does not ensure test order without `@FixMethodOrder` – Roger Oct 15 '15 at 16:59
  • 1
    @DavidWallace, as far as I know, the test class is instantiated for every test and methods marked `@Before` are run before every test, so I don't see a problem here with test order. Maybe the answer has been edited since your comment. Or do I get something wrong? – lex82 Oct 21 '16 at 13:27
  • Didn't know you could string together returns like that, very nice thank you! – EM-Creations Nov 13 '17 at 08:55
  • also if you use spy() you will need this: doReturn(obj1).doReturn(obj2).when(this.client).somemethod(); – fl0w Mar 07 '18 at 16:39
76

For all who search to return something and then for another call throw exception:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenReturn(obj2)
        .thenThrow(new RuntimeException("Fail"));

or

when(mockFoo.someMethod())
        .thenReturn(obj1, obj2)
        .thenThrow(new RuntimeException("Fail"));
djm.im
  • 3,295
  • 4
  • 30
  • 45
Nagy Attila
  • 1,119
  • 1
  • 10
  • 14
52

First of all don't make the mock static. Make it a private field. Just put your setUp class in the @Before not @BeforeClass. It might be run a bunch, but it's cheap.

Secondly, the way you have it right now is the correct way to get a mock to return something different depending on the test.

shoebox639
  • 2,312
  • 15
  • 14
45

Or, even cleaner:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);
pedromorfeu
  • 1,739
  • 14
  • 18
28

For Anyone using spy() and the doReturn() instead of the when() method:

what you need to return different object on different calls is this:

doReturn(obj1).doReturn(obj2).when(this.spyFoo).someMethod();

.

For classic mocks:

when(this.mockFoo.someMethod()).thenReturn(obj1, obj2);

or with an exception being thrown:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
fl0w
  • 3,593
  • 30
  • 34