0

I am new to Mockito and trying to understand how to use doAnswer in order to test a void method.

Here's my class with the onDestroy method to test:

public class TPresenter implements TContract.Presenter {
    private CompositeSubscription viewSubscription;

    //.......

    @Override public void onCreate(.......) {
        this.viewSubscription = new CompositeSubscription();
        //.......
    }

    @Override public void onDestroy() {
        if(viewSubscription != null && !viewSubscription.isUnsubscribed()) {
            viewSubscription.unsubscribe();
    }
}

Now I want to write a test for onDestroy() namely to verify that after executing onDestroy the subscription is unsubscribed. I found several examples to use doAnswer for testing void methods, for example here, and also here but I do not understand them.

Please show how to test the method onDestroy.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Monica
  • 389
  • 1
  • 7
  • 19

3 Answers3

2

The normal way how you could test your onDestroy() would be based on viewSubscription being a mocked object. And then you would do something like:

@Test
public testOnDestroyWithoutUnsubscribe() {
  when(mockedSubscription.isUnsubscribed()).thenReturn(false);
  //... trigger onDestroy()
  verifyNoMoreInteractions(mockedSubscription);
}

@Test
public testOnDestroyWithUnsubscribe() {
  when(mockedSubscription.isUnsubscribed()).thenReturn(true);
  //... trigger onDestroy()
  verify
  verify(mockedSubscription, times(1)).unsubscribe();
}

In other words: you create a mocked object, and you configure it to take both paths that are possible. Then you verify that the expected actions took place (or not, that is what the first test case does: ensure you do not unsubscribe).

Of course, you can't test the "subscription object is null" case (besides making it null, and ensuring that no NPE gets thrown when triggering the onDestroy()!

Given the comment by the OP: one doesn't necessarily have to use mocking here. But when you want to test a void method, your options are pretty limited. You have to observe side effects somehow!

If you can get a non-mocked viewSubscription instance to do that, fine, then do that. But if not, then somehow inserting a mocked instance is your next best choice. How to do the "dependency injection" depends on the exact context, such as the mocking/testing frameworks you are using.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Thanks for answering, but why do I need to mock the viewSubscription which is a private instance of my class TPresenter? I thought private instances of the class under test are real objects and are not mocked. – Monica Jan 15 '19 at 12:58
  • I tried but the tests do not pass. It seems that the method onDestroy is un-testable because viewSubscription is instantiated in onCreate. – Monica Jan 17 '19 at 21:56
  • 2
    @Monica Well, the "problem" here is the call to new. You can either user PowerMock(ito) to work around that, or you change to some other form of dependency injection. – GhostCat Jan 18 '19 at 11:47
0

Testing void methods in your main class under test is not a problem as does not require doAnswer.

Here is an example of how could you go about testing the call to unsubscribe.


import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


@RunWith(MockitoJUnitRunner.class)
public class TPresenterTest {

    @InjectMocks
    private TPresenter target = new TPresenter();

    @Mock
    private CompositeSubscription viewSubscription;


    @Test
    public void onDestroyShouldUnsubscribeWhenSubscriptionNotNullAndUnsubscribed() {
        when(viewSubscription.isUnsubscribed()).thenReturn(false);

        target.onDestroy();

        verify(viewSubscription).unsubscribe();
    }

    @Test
    public void onDestroyShouldNotUnsubscribeWhenSubscriptionNotNullAndNotUnsubscribed() {
        when(viewSubscription.isUnsubscribed()).thenReturn(true);

        target.onDestroy();

        verify(viewSubscription, never()).unsubscribe();
    }
}

Slav Pilus
  • 197
  • 1
  • 10
  • Thanks for answering, but I have the same question: why do I need to mock the viewSubscription which is a private instance of my class TPresenter? – Monica Jan 15 '19 at 13:00
  • 1
    The viewSubscription class is providing your class under test (TPresenter) with some functionality. From point of view of TPresenter is an external dependency. You don't need to mock it but it is sometimes just easier. Especially when there is some kind of logic of operation performed in your dependency. e.g. if your dependency is a service then it might be much easier to just mock it and to not execute actual body of it. If it is a POJO, on another hand, then you might choose just to instantiate it and set its fields. – Slav Pilus Jan 15 '19 at 14:19
0

As I mentioned in my comment to @GhostCat 's answer, my example is in fact un-testable because of the "new" instance of CompositeSubscription class. I would have to re-factor it and @GhostCat 's comment to his/her answer shows a way to do it.

Monica
  • 389
  • 1
  • 7
  • 19