javaunit-testingmvpmockitopassive-view

Using Mockito for unit-testing a presenter with a passive view


The problem

A presenter that "manages" a passive view subscribes to events that occur in that view (e.g. button click), and does not directly expose the methods that handle those events as public interface. I don't like the idea to make those methods public just for unit-testing since it smells like exposing the internal implementation details. Therefore, calling that event handling code becomes quite non-trivial.

My solution

The view mock has to "intercept" the event subscription and then the corresponding intercepted listener is used to call the event handling code. My implementation includes a utility class that implements the Answer interface from the Mockito API

   private class ArgumentRetrievingAnswer<TArg> implements Answer {           
          private TArg _arg;

          @Override
          public Object answer(InvocationOnMock invocation) {
                 Object[] args = invocation.getArguments();
                 _arg = (TArg)args[0];
                 return null;
          }

          public TArg getArg() {
                 return _arg;
          }

   }

The event subscription is intercepted in the following way

      XyzView xyzView = mock(XyzView.class);
      ArgumentRetrievingAnswer<OnEventListener> xyzViewTouchedListenerInterceptor = 
                   new ArgumentRetrievingAnswer<OnEventListener>();
      doAnswer(xyzViewTouchedListenerInterceptor)
             .when(xyzView).addViewTouchedListener(any(OnEventListener.class));

After creating the SUT instance...

XyzPresenter sut = new XyzPresenter(xyzView);

...I obtain the listener

OnEventListener xyzViewTouchListener = xyzViewTouchedListenerInterceptor.getArg();

In the "Act" part I call the event handling method of the listener

xyzViewTouchListener.onEvent();

The question

I'm quite new to unit testing in Java, so I'd like to know if there's any more elegant way of testing the presenter code. The current "Arrange" part is quite bloated an does not seem to excel in readability.

Edit: Adding the simplified SUT code on Jonathan's request. It illustrates that the presenter does not have any public methods (except constructor), and subscribes to the view events.

public interface XyzView {
    void setInfoPanelCaptionText(String text);

    void addViewInitializedListener(OnEventListener listener);
    void addViewTouchedListener(OnEventListener listener);
}


public class XyzPresenter {
    private XyzView _xyzView;

    private OnEventListener _xyzViewTouchedListener = new OnEventListener() {       
        @Override
        public void onEvent() {
            handleXyzViewTouch();   
        }
    };

    public XyzPresenter(XyzView xyzView) {
        _xyzView = xyzView;

        _xyzView.addViewTouchedListener(_xyzViewTouchedListener);
    }

    private void handleXyzViewTouch() {
        // event handling code
    }
}

Solution

  • Basically I also use ArgumentCaptor in this setup.

    The basic layout of my presenter tests is like this :

    @RunWith(MockitoJUnitRunner.class)
    public class PresenterTest {
    
        private Presenter sut;
    
        @Mock
        private View view;
        @Captor
        private ArgumentCaptor<ViewTouchedListener> listenerCaptor;
        private ViewTouchedListener listener;
    
        @Before
        public void setUp() {
            sut = new Presenter(view);
    
            verify(view).addViewTouchedListener(listenerCaptor.capture());
    
            listener = listenerCaptor.getValue();
        }
    
        // test methods have access to both sut and its registered listener
    }