In an attempt to orient myself and my colleagues with GWT, I've been reading up on GWT, trying things out, and blogging about them here. My goal is to start with simple use cases and build on to them with more complex ones. The way it's been working so far is that each post builds on the prior one.

Wednesday, February 17, 2010

Testing the Presenter

In the prior posts, we discussed a Passive View for the purposes of testability and also talked about using an Event Bus to decouple one UI component from another. Remember that the ContactDetailPresenter functionally will be responsible for 1) displaying a selected Contact and 2) enabling a Contact to be updated. Given the design we used to build out the presenter, we should now be able to test, in isolation, that it correctly does both of those things. “In isolation” means that we want to test that the ContactDetailPresenter does what it’s supposed to do without having using a ContactListPresenter in the test. Since this is an automated, non-interactive test and we don’t have an actual browser, it will be important that we can mock the ContactDetailView. We threw around the word “mock” loosely before, but now that we’re actually writing tests, we need to be more clear about what this means and how to do it. For the tests written in this example, we will use EasyMock and that library will be implementing many methods you see in the tests such as createMock(), expect(), andReturn(), etc. It is not the scope of the post to describe EasyMock in detail, but know that having a good approach to mocking objects is a critically important aspect to writing/maintaining a solid automated test suite.

The first test validates that the ContactDetailPresenter will send a selected Contact’s first name to the ContactDetailView. The test starts out by 1) setting up its expectations that the view will receive a first name. Then 2) simulates a Contact-selection by posting a ContactSelectedEvent to the event bus and lastly 3) verifies that the View did, in fact, have the selected Contact’s first name applied to it. Here is the test:


public void testContactSelected (){
EventBus eventBus = new EventBusImpl();

String firstName = "mockFirstName";

// set up a Contact with a particular first name
Contact contact = createMock(Contact.class);
expect(contact.getFirstName()).andReturn(firstName);

// 1) set up expectation that view should be set with the first name
ContactDetailView view = createMock(ContactDetailView.class);
HasChangeHandlers mockHasChangeHandlers = createMock(HasChangeHandlers.class);
expect(view.setFirstName(firstName)).andReturn(mockHasChangeHandlers);

replay(view, contact);

ContactDetailPresenter presenter = new ContactDetailPresenter(view, eventBus);

// 2) now fire event and ensure that view is set with first name
ContactSelectedEvent contactSelected = new ContactSelectedEvent(contact);
eventBus.fireEvent(contactSelected);

// 3) verify the View had the Contact’s first name set on it
verify(view);
}


The next test validates that a user updating the first name in the Contact Detail UI will cause the ContactDetailPresenter to 1) change the first name of the Contact in the Model and 2) emit an event that the Contact was updated. To accomplish this, the test will first mock up the presenter’s dependencies to behave as needed for the test. Next, the test will simulate the first name change. Lastly, the test will verify its expectations.




public void testContactUpdated(){
String firstNameBefore = "mockFirstNameBefore";
final String firstNameAfter = "mockFirstNameAfter";

// mocks up view to have a TextInput that our test can control
ContactDetailView view = createMock(ContactDetailView.class);
final TextInput firstNameInputMock = new TextInputMock();
expect(view.setFirstName(firstNameBefore)).andReturn(firstNameInputMock);

/*
* set up a mock Contact to accept a firstName on setFirstname and
* return that same first name on getFirstName.
*/

Contact contact = createMock(Contact.class);
final Capture firstNameCapturer = new Capture();
contact.setFirstName(capture(firstNameCapturer));
expectLastCall().anyTimes();
expect(contact.getFirstName()).andAnswer(new IAnswer() {

public String answer() throws Throwable {
return firstNameCapturer.getValue();
}

}).anyTimes();

replay(contact, view);

EventBus eventBus = new EventBusImpl();

// add handler to event bus to listen for ContactUpdatedEvents
final ContactUpdatedEvent[] updatedEvent = {null};
eventBus.addHandler(ContactUpdatedEvent.TYPE,
new ContactUpdatedEvent.ContactUpdatedEventHandler() {

public void handleEvent(ContactUpdatedEvent contactUpdatedEvent) {
updatedEvent[0] = contactUpdatedEvent;
}
});

// wire up presenter with its dependencies
ContactDetailPresenter presenter = new ContactDetailPresenter(view, eventBus);
contact.setFirstName(firstNameBefore);
presenter.setContact(contact);

// simulate the first name change on the TextInput
firstNameInputMock.setText(firstNameAfter);

// verify expectations
assertNotNull("The ContactUpdatedEvent should have been emitted when the text was changed on the input", updatedEvent[0]);
assertEquals("The name of the Contact should have been changed to the new name",
firstNameAfter,
updatedEvent[0].getContact().getFirstName());
}


This is extremely powerful. We’ve tested some core aspects of our interactive web application without actually having a user interface written or even established. We have not yet built anything that resembles HTML. Given this, we can really start seeing the flexibility of this design. Since most of the Contact List UI Component works and is testable with no View, it really illustrates how little we are locked in to a particular UI layout.



In this post we looked at techniques for testing a Presenter. This is a critical component to test since it contains a lot of UI logic and, due to our MVP design pattern, it is testable.

8 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This is a terrific article, and that I would really like additional info if you have got any. I’m fascinated with this subject and your post has been one among the simplest I actually have read.
    Java training in Chennai | Java training in Bangalore

    Java online training | Java training in Pune

    ReplyDelete
  3. You’ve written a really great article here. Your writing style makes this material easy to understand.. I agree with some of the many points you have made. Thank you for this is real thought-provoking content
    Data Science training in kalyan nagar | Data Science training in OMR

    Data Science training in chennai | Data science training in velachery

    Data science training in tambaram | Data science training in jaya nagar

    ReplyDelete
  4. I’m planning to start my blog soon, but I’m a little lost on everything. Would you suggest starting with a free platform like Word Press or go for a paid option? There are so many choices out there that I’m completely confused.nice blog.
    Ai & Artificial Intelligence Course in Chennai
    PHP Training in Chennai
    Ethical Hacking Course in Chennai Blue Prism Training in Chennai
    UiPath Training in Chennai

    ReplyDelete

Followers