Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reusing mock behavior / best practice in Mockito?

I'll preface this with the background that I am very green and don't have much experience with testing in general, and have never used a mocking framework before.

I am writing unit tests for code that hits a lot of different web services, which I have mocked. Many of my tests verify the result of a call where all of my service calls are successful except a call to ServiceX. My first instinct was to set up happy-path behavior for all mocks in a @Before block and then modify the mock behavior for each test.

@Before
public void init(){
    when(serviceA.doSomething()).thenReturn(true);
    when(serviceB.doSomething()).thenReturn(true);
    when(serviceC.doSomething()).thenReturn(true);
    when(serviceD.doSomething()).thenReturn(true);
    when(serviceE.doSomething()).thenReturn(true);
}

@Test
public void testDoBusinessSuccess(){
    String result = businessLogic.doBusiness();
    assertThat(result, is("success"));
}

@Test
public void testDoBusinessFailureWhenServiceAFails(){
    when(serviceA.doSomething()).thenReturn(false);

    String result = businessLogic.doBusiness();
    assertThat(result, is("service A is down!"));
}

@Test
public void testDoBusinessFailureWhenServiceBFails(){
    when(serviceB.doSomething()).thenReturn(false);

...

which makes each test case succinct, and it is easy to see what is being tested, since I only specify the behavior which deviates from the norm.

But I suspect that this is not how Mockito wants me to set up mock behaviors, because when I tried to verify that a failure in ServiceB means that ServiceC is never hit, I realized that my calls to when(serviceC.doSomething()) in the @Before counted as invocations on serviceC. That is, my verifyZeroInteractions(serviceC) always failed because I had called when(serviceC.doSomething()).thenReturn(true), even when the test case never touched serviceC otherwise.

So what is the best practice? Am I better off just explicitly setting up the behavior for every mock in every test, even though I'd be repeating nearly the same 5 lines all over the place?

like image 803
PotataChipz Avatar asked Nov 03 '22 13:11

PotataChipz


1 Answers

when(serviceC.doSomething()) in the @Before counted as invocations on serviceC

I doubt that the when construct is treated as an invocation because that is just stubbing.
Can you double check your code to see whether serviceC.doSomething is called from within your @Test

Regarding the best practice, I think you should only move in stubbing behaviour which is common for all test cases into @Before

As an aside from the way your code is looking I think you can try refactoring into Chain of Responsibility pattern and this should help you in coding the test code.

like image 124
Ajay George Avatar answered Nov 08 '22 05:11

Ajay George