1
votes

I'm trying to test the start method of an Activity that uses RequestFactory.

I manage to test RF calls invoking directly the service using this article example, but I'm missing something mocking RF calls called from the tested activity.

It's more clear with the code.

EDIT : more specific

What I really want to know, is how to replace the response of a Receiver method (onSuccess,onFailure...) called in an Activity? This way I would be able to test the code inside the receiver method.

So basically here is my activity :

public class MyActivity extends AbstractActivity implements MyView.Presenter {
    private List<MyEntityProxy> entities;
    private MyView view;
    private ClientFactory cf;
    private EntityRequest entityRequest;
    private AppRequestFactory rf;

    @Inject
    public ClientsListActivity(ClientsListViewEditor view, ClientFactory clientFactory) {
        this.view = view;
        this.clientFactory = clientFactory;
        rf = clientFactory.getRequestFactory();
    }

    @Override
    public void start(final AcceptsOneWidget panel, EventBus eventBus) {
        view.setPresenter(this);
        refreshEntities();
    }

    public void refreshEntities(){
         entityRequest = rf.entityRequest();
         entityRequest.getAll().with("opt1,"opt2").fire(new Receiver<List<MyEntityProxy>>() {
             @Override
            public void onSuccess(List<MyEntityProxy> response) {
                entities = response;
                entityRequest = requestFactory.clientRequest();
             }
         });
    }
    public List<MyEntityProxy> getEntities(){
        return entities;
    }
}

To test it in JUnit I use GwtMockito, so here is the test class MyActivityTest :

@RunWith(GwtMockitoTestRunner.class)
public class ClientListActivityTest{

    private MyActivity activity;
    private EventBus eventBus;
    private AppRequestFactory rf;
    @GwtMock
    private ClientFactory cf;
    @GwtMock
    private MyView;
    @GwtMock
    private AcceptsOneWidget panel;

    @Before
    public void setUp(){
        eventBus = new SimpleEventBus();
        rf = RequestFactoryHelper.create(AppRequestFactory.class);
        cf = new ClientFactory(eventBus,rf);
        activity = new MyActivity(view,cf);

    }

    @Test
    public void testStartActivity(){
        List<EntityProxy> result = new ArrayList<EntityProxy>();
        EntityProxy expectedClient = mock(EntityProxy.class);
        expectedEntity.setNom("Client 1");
        EntityProxy expectedClient2 = mock(EntityProxy.class);
        expectedEntity.setNom("Client 2");
        result.add(expectedEntity);
        result.add(expectedEntity2);
        //Here I have to change the requestFactory Call, so I try that but without success :
        Request<?> req = mock(Request.class);
        doReturn(req).when(mock(MyEntityRequest.class)).getAll();

        doAnswer(RequestFactoryHelper.ok(result)).when(req).fire(any(Receiver.class));
        activity.start(panel, eventBus);
        assertEquals(activity.getEntities().size(),2); //This Test fails size = 0
    }


}

My RequestFactoryHelper (inspired from here ) :

public class RequestFactoryHelper {

    private static class MockServiceLocator implements ServiceLocator {
        private final Map<Class<?>, Object> services = new HashMap<Class<?>, Object>();

        @Override
        public Object getInstance( Class<?> clazz ) {
            // Make sure to return always the same mocked instance for each requested type
            Object result = services.get( clazz );
            if (result == null) {
                result = mock( clazz );
                services.put( clazz, result );
            }
            return result;
        }
    }

    private static class MockServiceDecorator extends ServiceLayerDecorator {
        @Override
        public <T extends ServiceLocator> T createServiceLocator( Class<T> clazz ) {
            return (T) serviceLocator;
        }
    }

    private static MockServiceLocator serviceLocator = new MockServiceLocator();
    private static ServiceLayer serviceLayer = ServiceLayer.create( new MockServiceDecorator() );

    /**
     * Creates a {@link RequestFactory}.
     */
    public static <T extends RequestFactory> T create( Class<T> requestFactoryClass ) {
        SimpleRequestProcessor processor = new SimpleRequestProcessor( serviceLayer );
        T factory = RequestFactorySource.create( requestFactoryClass );
        factory.initialize( new SimpleEventBus(), new InProcessRequestTransport( processor ) );
        return factory;
    }

    /**
     * Returns the same service instance as used by the RequestFactory internals.
     */
    public static <T> T getService( Class<T> serviceClass ) {
        T result = (T) serviceLocator.getInstance( serviceClass );
        reset( result ); // reset mock to avoid side effects when used in multiple tests
        return result;
    }

   /**
    * Returns the value passed to {@link Receiver#onSuccess(Object)}
    */
    public static <T> T captureResult( Receiver<T> receiver ) {
        ArgumentCaptor<Object> captor = ArgumentCaptor.forClass( Object.class );
        verify( receiver ).onSuccess( (T) captor.capture() );
        return (T) captor.getValue();
    }

    public static <T> Answer<T> ok(final T result) {
         return new Answer<T>() {

            @Override
            public T answer(InvocationOnMock invocation) throws Throwable {
               Object[] args = invocation.getArguments();
               Object _receiver = args[args.length - 1];
               Receiver<T> receiver = (Receiver<T>)_receiver;
               receiver.onSuccess(result);
               return null;
            }

        };
    }
}
1
Can you be more specific on the problem you are having? - Chris Hinshaw
I will edit my question to specify. In my test I want to test the refresh() method, that used to get entities from the server trough RF call. So I need to replace the response in the onSuccess() method of the Receiver. After that I will be able to test the code inside the onSuccess Method depending on my response replacement in the test. Is it more clear? :s - Pintouch
BTW, great question. I have gone down the same route as you trying to mock requestfactory methods with no avail. If I understand your problem it is with the ok method returning the mocked result. I came up with another solution that worked very well in my case. I either use AsyncDataProvider classes or a Command pattern where I can easily mock the response from the command. This can also give you an easy hook for caching. I know this doesn't solve your problem but it is a nice work around with some added benefits. There is a video from Ray Ryan that explains how google was using Command patter - Chris Hinshaw
The ok method works if I create the Receiver in the test method :Receiver<List<MyEntityProxy>> rec = mock(Receiver.class);then I call doAnswer(RequestFactoryHelper.ok(result)).when(req).fire(any(Receiver.class));and I can catch the result using List<MyEntityProxy> result = RequestFactoryHelper.captureResult(receiver); But doing that I'm not testing the code inside my activity. - Pintouch

1 Answers

1
votes

This is how I tested the Receiver method "onSuccess". I created a custom Answer for Mockito.doAnswer.

The code to test.

public void myMethod(String arg1, String arg2) {
    requestFactory.adminRequest().someMethod(arg1, arg2).fire(new Receiver<Void>() {
        @Override
        public void onSuccess(Void response) {
            placeController.goTo(new MyPlace());
        }
    });
}

The test.

@Test
public void testMyMethod() {
    String arg1 = "arg1";
    String arg2 = "arg2";
    when(requestFactory.adminRequest()).thenReturn(adminRequest);
    when(adminRequest.someMethod(arg1, arg2)).thenReturn(request);
    doAnswer(new Answer<Void>() {

        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] args = invocation.getArguments();
            Receiver<Void> receiver = (Receiver<Void>) args[0];
            receiver.onSuccess(null);
            return null;
        }
    }).when(request).fire(any(Receiver.class));

    myActivity.myMethod(arg1, arg2);
    verify(adminRequest).someMethod(arg1, arg2);
    verify(request).fire(any(Receiver.class));
    verify(placeController).goTo(any(myPlace.class));

}

requestFactory, adminRequest, request and placeController are all mocks.