3

I want to mock a dependency and return a default value in most test cases since most of them should not care about the values returned but there are some certain cases like I would like to test like the dependency returns some weird values or just throw. So I am modeling it in this way. Most cases, it should return a nice and valid value.

Test Setup which return the 20L by default for all test classes.

Dependency dependency = Mockito.mock(Dependency.class);
when(dependency.returnSomeVal()).thenReturn(20L);

In a specific test cases class, I would like to override the behavior like below:

when(dependency.returnSomeVal()).thenThrow(); //failure cases
when(dependency.returnSomeVal()).thenReturn(Weird_Val); //failure cases

But I don't find a good solution to override the existing behavior? Any idea?

Progman
  • 16,827
  • 6
  • 33
  • 48
dashenswen
  • 540
  • 2
  • 4
  • 21
  • 1
    Does this answer your question? [Mockito - difference between doReturn() and when()](https://stackoverflow.com/questions/20353846/mockito-difference-between-doreturn-and-when) – Progman Jun 15 '20 at 22:57
  • @Progman I am not sure how it solves my problelm. it's basically talking about the difference between doReturn and thenReturn? – dashenswen Jun 15 '20 at 23:32
  • `doReturn` allows you to change the behavior of the mock without actually calling the `returnSomeVal()` method, which you would do when you use `thenReturn()`. That way you can just change it to do something else (return a different value, throw an exception, etc...). – Progman Jun 15 '20 at 23:37
  • @Progman I see. So you are suggesting to use thenReturn for default behavior and doReturn for failure cases if I understand it correctly? – dashenswen Jun 16 '20 at 15:13
  • No. When you write `dependency.returnSomeVal()` you will make a method call, be it on the original method or on the mocked method. If you do not want that during the mockup setup you can use `doReturn()`. This will setup the mockup without calling the method you want to mock. Try to run the two lines with `thenThrow()` and `thenReturn(Weird_val);` and you will see that you will get an exception even though you just wanted to configure the mock with these lines. – Progman Jun 16 '20 at 23:25

2 Answers2

3

You can reset the mock and add behavior. In the test, do

Mockito.reset(dependency);
when(dependency.returnSomeVal()).thenThrow(); //failure cases
when(dependency.returnSomeVal()).thenReturn(Weird_Val); //failure cases

Resetting will remove all mocked behavior on this class though. If you want to remock only some methods, then you have to create the mock from scratch.

Hari Menon
  • 33,649
  • 14
  • 85
  • 108
3

I ended using myself this pattern to mock a bunch of methods of a class providing configurations.

In a @Before method I setup a bunch of stubs for a mocked object that provide a correct configuration for each test. Afterwards, in each test it was extremely convenient to only override one of those stubs to provide a different configuration and test a different error case.

I think the response from Hari Menon is correct but it somehow defeats the purpose explained in the question. If the mock is reset, all the stubs would need to be added again, making this pattern very confusing (it would be better to not use any overriding than using reset in this case, the code would be way more straightforward).

The comments added to the question provide indeed an indirect answer on how to achieve this, and why it works, but it took me a bit to get it working.

In spite of one of the comments, I made everything work by using in my @Before fixture when().thenReturn() and overriding the concrete stub with doReturn().when()

Example:

public class WorkerTest {

    private ConfigProvider mockedConfigProvider = mock(ConfigProvider.class);

    @Before
    public void setup() {
    // Setup stubs with a correct config
        when(mockedConfigProvider.getValue("property1")).thenReturn("value1");
        when(mockedConfigProvider.getValue("property2")).thenReturn("value2");
        when(mockedConfigProvider.getValue("property3")).thenReturn("value3");
        when(mockedConfigProvider.getValue("property4")).thenReturn("value4");
    }

    @Test
    public void test_GoodConfig(){
        // The config object gets injected in the test worker
        Worker testWorker = new Worker(mockedConfigProvider);
        // testWorker.execute() returns true if everything went well
        assertTrue(testWorker.execute());
    }

    @Test
    public void test_BadConfigProp1(){
        // Test now with a broken 'property1', overriding that stub.
        doReturn(null).when(mockedConfigProvider).getValue("property1");
        Worker testWorker = new Worker(mockedConfigProvider);
        // testWorker.execute() returns false if there is a problem.
        assertFalse(testWorker.execute());
    }

    @Test
    public void test_BadConfigProp2(){
    // This test needs to only override the result of property2
    doReturn("crazy result").when(mockedConfigProvider).getValue("property2");
    ...

}
Telegrapher
  • 330
  • 4
  • 11