简体   繁体   中英

Test Case for GWT MVP with Activities and Places and GIN using Mockito

I am new to GWT MVP Pattern with Activities and Places with the combination of GIN. I have started attempting to write a JUnit test case for my GWT project using Mockito. A lot of blogs suggested that I do not use GWT Test Case because of its performance so I planned on sticking to Mockito. I am writing a test case for one of the Presenters. Since I am using GIN to create instance for most of the things in my Presenter I have to mock my Gin Injector object. My Junit Test Case is not allowing me to mock the Gin injector. I read somewhere that we cannot use Gin in the Junit Test Case instead we have to go with Guice. My question is that how do I mock my Gin injector using Mockito? I found some Test Case which used the exact same pattern that I did for my project except that they used Client Factory instead. I have failed in replacing the Client factory with GIN in the test case. The code that I found online is as follows and I have to replace Client Factory with GIN injector in my Test Case.

@RunWith(MockitoJUnitRunner.class) public class ContactListActivityTest {

@Mock
private IClientFactory clientFactoryMock;

@Mock
private PlaceController placeControllerMock;

@Mock
private IContactListView contactListViewMock;

@Mock
private AcceptsOneWidget acceptsOneWidgetMock;

@Mock
private IContactServiceAsync contactServiceAsyncMock;

@Mock
private EventBus eventBusMock;

private List<Contact> contacts;
private Contact contact1;
private Contact contact2;

@SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception {
    when(clientFactoryMock.getPlaceController()).thenReturn(placeControllerMock);
    when(clientFactoryMock.getContactListView()).thenReturn(contactListViewMock);
    when(clientFactoryMock.getContactService()).thenReturn(contactServiceAsyncMock);

    Answer<Void> answer = new Answer<Void>() {
        @Override
        public Void answer(InvocationOnMock invocation) {
            Object[] args = invocation.getArguments();
            AsyncCallback<List<Contact>> asyncCallback = (AsyncCallback<List<Contact>>) args[0];
            contact1 = new Contact();
            contact1.setFirstName("Kai");
            contact1.setLastName("Toedter");
            contact1.setEmail("kai@toedter.com");
            contact2 = new Contact();
            contact2.setFirstName("Kai2");
            contact2.setLastName("Toedter2");
            contact2.setEmail("kai2@toedter.com");
            final List<Contact> contacts2 = new ArrayList<Contact>();
            contacts2.add(contact1);
            contacts2.add(contact2);
            asyncCallback.onSuccess(contacts2);
            return null;
        }
    };

    doAnswer(answer).when(contactServiceAsyncMock).getAllContacts(any(AsyncCallback.class));

    // set the real contacts object, when clientFactory.setContacts is
    // called
    Answer<Void> setContactsAnswer = new Answer<Void>() {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            contacts = (List<Contact>) invocation.getArguments()[0];
            // System.out.println("answer() to setContacts(): " + contacts);
            return null;
        }
    };

    doAnswer(setContactsAnswer).when(clientFactoryMock).setContacts(any(List.class));

    // Return the real contacts object, when clientFactory.getContacts is
    // called
    Answer<List<Contact>> getContactsAnswer = new Answer<List<Contact>>() {
        @Override
        public List<Contact> answer(InvocationOnMock invocation) throws Throwable {
            return contacts;
        }
    };

    doAnswer(getContactsAnswer).when(clientFactoryMock).getContacts();
}

@Test
public void testGotoPlace() {
    ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(null), clientFactoryMock);

    ContactPlace contactPlace = new ContactPlace("kai@toedter.com");
    contactListActivity.goTo(contactPlace);

    verify(placeControllerMock).goTo(contactPlace);
}

@Test
public void testStartWithEmptyToken() {
    clientFactoryMock.setContacts(null); // force RCP
    ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(""), clientFactoryMock);
    contactListActivity.start(acceptsOneWidgetMock, eventBusMock);

    verify(contactListViewMock).setPresenter(contactListActivity);
    verify(contactListViewMock).initialize(contacts);
}

@Test
public void testStartWithToken() {
    String token = "kai@toedter.com";
    clientFactoryMock.setContacts(null); // force RCP

    ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(token), clientFactoryMock);
    contactListActivity.start(acceptsOneWidgetMock, eventBusMock);

    verify(contactListViewMock).setPresenter(contactListActivity);
    verify(contactListViewMock).initialize(contacts);
    verify(contactListViewMock).selectInitialContact(contact1);
    verify(eventBusMock).fireEvent(any(ContactViewEvent.class));
}

@Test
public void testMayStop() {
    ContactListActivity contactListActivity = new ContactListActivity(new ContactPlace(null), clientFactoryMock);
    contactListActivity.start(acceptsOneWidgetMock, eventBusMock);
    contactListActivity.mayStop();

    verify(contactListViewMock).setPresenter(null);
}

@Test
public void clientFactoryTest() {
    List<Contact> testList = new ArrayList<Contact>();
    clientFactoryMock.setContacts(testList);
    Assert.assertNotNull(clientFactoryMock.getContacts());
}

}

Please Help.

If your code depends on the Ginjector , then you have a problem: you're not injecting direct dependencies . If you need a factory of objects, inject a Provider .

But in your case, IClientFactory also serves as a shared state , a value holder or local cache for a List<Contact> ; which means IClientFactory violates the single responsibility principle .

So, first extract the local cache responsibility into its own object (eg a ContactListCache object, or a more generic ValueHolder<List<Contact>> ) then inject an instance of that object.
And of course, inject the PlaceController , view and GWT-RPC service directly.

But actually, I'd go farther and refactor the code to extract the retrieve from cache or ask the server responsibility into its own ContactListHolder object (or, as you're using GWT-RPC, you could implement the IContactServiceAsync interface as a wrapper around the one generated by GWT.create() and that adds the caching behavior; see http://www.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html for some inspiration). That would simplify the activity's code a lot.


As a side note, this code probable makes too much use of mocking: why not using the real PlaceController (and spy() ing its goTo(Place) method) and a SimpleEventBus or CountingEventBus ?

What I have done is to use which is an extension of Mockito. ,它是的扩展。 In the only one class that I use my injector, my test looks like this

@RunWith(GwtMockitoTestRunner.class)
public class AppControllerTest {

    AppController controller;

    @Mock
    EventBus eventBus;

    @Mock
    AppInjector injector;

    @Before
    public void setUp() throws Exception {
    }

    @Test
    public void shouldUseInjector() throws Exception {
        // Given
        controller = new AppController(eventBus, injector);

        // When
        controller.go();

        // Verify
        verify(injector).mainPresenter();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM