what a cool fog

The model-view-presenter (aka MVP) architecture pattern is a good choice for Vaadin applications. It clearly separates the business logic from the UI code and improves testability. Well… I heard statements like that many times before without proper explanations or examples what makes a pattern so useful that we cannot start a new project without using it. With this post I want to show how developers can use a MVP architecture and properly write unit tests to create stable projects with a solid project and code quality.

This is the third part in a series of blog posts on best practices using the Vaadin 8 UI framework to create web applications. The code for a small sample application that I use for all these blog posts (details can be found in the other posts) can be downloaded from https://bitbucket.org/mekaso/vaadin-cdi-mvp/branch/vaadin8

  1. Use the Vaadin CDI Navigator to support bookmarks and browser navigation buttons
  2. A practical guide to MVP for Vaadin 8
  3. Unit tests for a Vaadin 8 MVP application

The application makes use of CDI and the Vaadin navigator with a CDIViewResolver. These are cool features of the Vaadin framework and the code of this small application looks solid and elegant. However, we need something to mock or instantiate the classes that rely on the CDI or web containers that are used in the application.

I checked out a lot of libraries for testing, mocking and CDI support and came up with these choices:

  • JUnit for unit tests (the “default” and already integrated in a lot of IDEs, Test-NG is also a good (or even better) choice)
  • CDI-Unit for creating unit tests for CDI driven applications (Apache deltaspike test control module should also work)
  • JMockit for mocking objects and methods (does easily integrate with other tests frameworks and does not need a JUnit runner)

The following paragraphs show how I created tests for backend services and the frontend layer with presenter and view classes.

Testing backend services

The small sample application has only one service – the PersonService class. To test the methods of PersonService, we just have to create a test class and annotate the class with @RunWith(CdiRunner.class). By using the CdiRunner class of the CDI unit framework, we can use dependeny injection annotations just like we do in the application and simply inject the service we want to test in the test class.

import static org.junit.Assert.*;
import java.util.List;
import javax.inject.Inject;
import org.jglue.cdiunit.CdiRunner;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(CdiRunner.class)
public class PersonServiceTest {

  @Inject
  private PersonService personService;
  
  @Test
  public void testFindAll() {
    List<Person> personList = this.personService.findAll();
    assertNotNull(personList);
    assertTrue(personList.size() > 0);
  }
  ...
}

Testing the business logic (presenter)

To test a presenter with all its methods, we need to register and activate the Vaadin CDI scopes to the CDI container that is used for testing. I found a solution for this task on the web and simply included the VaadinContextExtension class in the project. It can be activated by using the annotation @AdditionalClasses({VaadinContextExtension.class}) at class level of the presenter test class. Now we can inject the model, view and presenter classes into the test class.

@RunWith(CdiRunner.class)
@AdditionalClasses({VaadinContextExtension.class})
public class MasterPresenterTest {
  @Inject
  private MasterPresenter presenter;
  @Inject
  private MasterView view;
  @Inject
  private MasterViewModel model;
  ...
  /**
   * Test the presener.init() method. The model should contain 3 persons after the init() method is called.
   */
  @Test
  @InRequestScope
  public void testInit() {
    assertNull(this.model.getPersonList());
    this.presenter.init(new InitMasterViewEvent());
    assertNotNull(this.model.getPersonList());
    assertTrue(this.model.getPersonList().size() == 3);
  }
}

Vaadin uses all kinds of static methods (e.g. getCurrent()) in its core classes Page, UI or VaadinServlet. In test classes, that run outside of a web or JEE container, we need to mock or create all kinds of classes to fully use all Vaadin features for our testing needs. If we want to test a presenter method, we first need to use CDI-unit’s @InRequestScope annotation for all test methods. This will let the test framework know, that there is an initialized servlet request that lasts from start until the end of the test method.

After I started the unit test I got an exception from a Vaadin class, because it usually runs inside a JEE or web container. To avoid such an exception, I needed to mock the VaadinServlet class that caused the exception. Mocking such classes can be a hard job (sometimes), but for this task I only had to mock the ServletContext (just use the MockServletContext class from CDI unit) and the VaadinServlet class with the help of the JMockit framework.

/**
 * Mock the VaadinServlet and a ServletContext to avoid exceptions.
 */
@BeforeClass
public static void initClass() {
  ServletContext servletContext = new MockServletContextImpl();
  new MockUp() {
    @Mock
    public VaadinServlet getCurrent() {
      return new VaadinServlet();
    }
    @Mock
    public ServletContext getServletContext() {
      return servletContext;
    }
  };
}

Whenever the code VaadinServlet.getCurrent().getServletContext() is called inside our unit test, it will return an MockServletContextImpl object and will not throw a NullPointerException. These are the only classes I needed to mock for running the unit tests for the presenter class.

Testing the user interface (views)

Testing the view classes in a Vaadin MVP architecture is just a bit more complicated than testing a presenter class. You just need to mock a little more classes (ServletContext or VaadinServlet) and ensure that the view you would like to test is actually entered. To enter the view you need to call the enter(ViewChangeEvent event) method of the View interface.

private void enterView() {
  Navigator navigator = new Navigator(this.ui, this.ui);
  ViewChangeEvent event = new ViewChangeEvent(navigator , null, this.view, MasterView.VIEW_NAME, "");
  this.view.enter(event);
}

The UI and view classes are injected automatically in our unit test class. We just need to create a Navigator to create a ViewChangeEvent and use it for the enter method of the view. Now the enter method of our view is called and we can test the behaviour of the view elements. To test the elements of the view the root layout of the view elements is declared as protected in the view class (protected MasterLayoutImpl layout). Lets say we would like to test the behaviour of the select button on the view. The select button is only enabled if the user selected a row in the grid. Here is the code for this test:

@Test
@InRequestScope
public void testTableSelect() {
  enterView();
  Button editButton = this.view.layout.getEditButton();
  Grid personGrid = this.view.layout.getPersonGrid();
  // button should be disabled by default
  assertFalse(editButton.isEnabled());
  Person person = this.model.getPersonList().get(0);
  // select a row
  personGrid.select(person);
  // button should be enabled
  assertTrue(editButton.isEnabled());
  // unselect the row
  personGrid.getSelectionModel().deselect(person);
  assertFalse(editButton.isEnabled());
}

This is quite a straight-forward approach to test the view elements. The code is easy readable and straight to the point. By testing the view in this way we can testify that the application works correct, if the UI framework (Vaadin) works correctly. To ensure that, we need to create some end-user UI tests with Selenium or the Vaadin testbench.

By Meik Kaufmann

I am a certified oracle enterprise architect and like to share my knowledge and experience that I gain in the projects I am working on.

Leave a Reply

Your email address will not be published. Required fields are marked *