1

I am having a service whose method looks somewhat like this -

class ServiceClass {

    @Inject
    EventService event;

    public void handleUser(User user) throws ServiceClassException {
        Customer customer = userToCustomer(user);
        CustomerDetail detail = userToCustomerDetail(user);

        try {
            Response response1 = event.generateEvent(customer);
        } catch(Exception e) {
            throw new ServiceClassException();
        }

        try {
            Response response2 = event.generateEvent(detail);
        } catch(Exception e) {
            throw new ServiceClassException();
        } 
    }

    private Customer userToCustomer(User user) {
        // Some logic
    }

    private CustomerDetail userToCustomerDetail(User user) {
        // Some logic
    }
}

Now while writing the test, I wanna check the exception handling. This is how my test case looks like.

class ServiceClassTest {

    @InjectMocks
    ServiceClass service;

    @Mock
    EventService event;

    @Before
    public void setUp() {
        User user = new User();
        user.setValue("fsfw");
    }

    @Test
    public void handleUserThrowsException() throws Exception {
        Mockito.when(event.generateEvent(Mockito.any(Customer.class))).thenThrow(new RuntimeException());

        try {
            Response response1 = service.handleUser(user);
        } catch(ServiceClassException e) {}

        Mockito.verify(event, Mockito.never()).generateEvent(Mockito.any(CustomerDetail.class));
    }
}

The above test case is failing with the message Never wanted here:, But invoked here:

Since both are of type any it is unable to differentiate between which method should not be executed.

I have tried different variations like replacing the line - Mockito.when(event.generateEvent(Mockito.any(Customer.class))).thenThrow(new RuntimeException());

with

Mockito.when(event.generateEvent(Customer.class)).thenThrow(new RuntimeException());

But it is not working because the Customer object is something which is created inside handleUser method and so both the instance are different and it is not throwing Exception which I setup in the Test.

Similar with,

Customer customer = new Customer;
customer.setValue("fsfw");
Mockito.when(event.generateEvent(customer)).thenThrow(new RuntimeException());

Again since both the Customer instance are different it is not throwing Exception as it is suppose to according to the Test setup.

Can any of you suggest how to test this one? Writing a test case like this works, but I don't know if this is the correct way to do it or not -

@Test
public void handleUserThrowsException() throws Exception {
    Mockito.when(event.generateEvent(Mockito.any(Customer.class))).thenThrow(new RuntimeException());

    try {
        Response response1 = service.handleUser(user);
    } catch(ServiceClassException e) {}

    Mockito.verify(event, Mockito.never()).generateEvent(CustomerDetail.class);
}

Please let me know the correct way to do this kind of testing?

Rito
  • 3,092
  • 2
  • 27
  • 40

1 Answers1

1

Two thoughts here:

  • is it really mandatory for you to verify those non-interactions? In other words: is it really helpful to verify that aspect at all?
  • and when that is the case: why focus on that special case?

In other words:

  • create a mocking spec for the expected calls
  • use verifyNoMoreInteractions on your mock instead (see here for further reading).

SO: specify all expected calls, and verify that nothing else happened. Gives you the same result, and is easy to write down, and easy to comprehend.

Thanks @GhostCat for the answer, just want to add these two lines of code which I used to verify this test scenario as suggested by you -

    @Test
    public void handleUserThrowsException() throws Exception {
        Mockito.when(event.generateEvent(Mockito.any())).thenThrow(new RuntimeException());

        try {
            Response response1 = service.handleUser(user);
        } catch(ServiceClassException e) {}

        Mockito.verify(event, Mockito.times(1)).generateEvent(Mockito.any());
        Mockito.verifyNoMoreInteractions(event);
    }
Rito
  • 3,092
  • 2
  • 27
  • 40
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • I am not sure if I got your question correctly, but the reason I am verifying the non-interactions is because I want to make sure that after I get an exception in this line `event.generateEvent(customer)` I shouldn't proceed forward with the code. That's why I am making sure that `event.generateEvent(detail)` is not getting called. – Rito Sep 11 '17 at 08:52
  • As said: then **only** specify the expected calls, and verify **no other** things happen. "no other" in general, instead of "not A, not B, not C, ...". Don't "exclude specific calls" - exclude *any* other call! – GhostCat Sep 11 '17 at 08:53
  • I have never used `verifyNoMoreInteractions` before so please let me know if this solution looks correct to you. I have replaced this line `Mockito.verify(event, Mockito.never()).generateEvent(Mockito.any(CustomerDetail.class));` with two verifications - `Mockito.verify(event, Mockito.atLeastOnce()).generateEvent(Mockito.any());` `Mockito.verifyNoMoreInteractions(event);` The test is getting passed correctly now. Please let me know if this is what you were suggesting. – Rito Sep 11 '17 at 09:13
  • I am not sure if that will work - this allows that `generateEvent()` is invoked **any** times (> 0). You want something like times(1) in there! – GhostCat Sep 11 '17 at 09:19