6

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?

PotataChipz
  • 505
  • 5
  • 11

3 Answers3

2
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.

Community
  • 1
  • 1
Ajay George
  • 11,759
  • 1
  • 40
  • 48
  • You are correct. Without thinking, I had initialized my mocks statically, so of course the invocations from previous tests were being counted against me. I tested that removing the when() on service C caused the verifyZeroInteractions to succeed, but when I did that I commented out the earlier tests because I had changed the behavior and knew they would fail. D'oh! Good reminder to not trust my own assumptions so easily. Thanks! – PotataChipz Oct 25 '12 at 05:12
0

This is not specific to mocking but only code that is common to every test should go in @Before since it gets executed before every junit test.

So if you want behavior specific to tests, all that mocking stuff should be in those tests.

If you have behavior common to more than 1 test you can extract that into a method which is called by only those tests that need that behavior.

smk
  • 5,340
  • 5
  • 27
  • 41
0

The invocation that verify is counting is the one in the test method itself. Change the call in the each method to look like this.

doReturn(false).when(serviceA).doSomething();

This will have the same effect as what you're doing in terms of what gets stubbed; but it won't be counted as an invocation of doSomething for the purposes of verification.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110