Unit testing the GWT code

MVP's loose coupling enables rapid development, as the view implementation, server-side service implementation, and presenters are independent of each other. Hence, developers can concentrate on different areas of the application without stepping on each other, for instance, one can work on the server-side business logic, work on the presentation layer logic, and implement the view logic. View implementation doesn't contain any business logic other than UI components and layout information. So no JUnit test is required for the view implementation; only manual inspection is good enough. However, the presentation layer contains business logic, such as, a user cannot post a negative amount while making the payment for a bill or the payment amount cannot exceed the payable amount. Mockito plays a key role in mocking DOM widgets and stubbing widget behaviors. We'll refactor the DetailsPresenter class and extract the anonymous DOM click handler out of the constructor and create a new handler class. The following ClickHandler class performs the validation logic:

public class PaymentButtonClickHandler implements ClickHandler {
  private DetailsPresenter presenter;
  public PaymentButtonClickHandler(DetailsPresenter detailsPresenter) {
    this.presenter = detailsPresenter;
  }

  @Override
  public void onClick(final ClickEvent event) {
    String amount = presenter.getDetailsView().getPaymentAmount().getValue();
    if(amount == null || "".equals(amount)){
      Window.alert("Please enter a payment amount");
      return;
    }

    BigDecimal paymentAmt = null;
    try{
      double amtDbl = Double.parseDouble(amount);
      paymentAmt = new BigDecimal(amtDbl);
    }catch(NumberFormatException exception){
      Window.alert("Please enter a valid payment amount");
      return;
    }
    if(paymentAmt.compareTo(BigDecimal.ZERO) <= 0){
      Window.alert("Please enter a positive payment amount");
      return;
    }

    if(presenter.getDetailsView().getOutstandingAmount().compareTo(paymentAmt) < 0){
      Window.alert("Payment amount cannot exceed the payable amount");
      return;
    }
    ((Button)event.getSource()).setEnabled(false);
    presenter.makePayment(paymentAmt);
  }
}

Modify the DetailsPresenter class to call this click handler. Create a JUnit test PaymentButtonClickHandlerTest under the test source folder and the com.packt.billing.client.event package. You cannot mock the static call to the Window.alert() method. If you just write Window.alert() in your JUnit test and run the test, you will encounter an UnsatisfiedLinkError exception, as shown in the following screenshot:

Unit testing the GWT code

We'll use PowerMock to disable the static calls to the Window.alert() method. Add the associated JAR files to the project classpath and modify the test as follows:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Window.class)
public class PaymentButtonClickHandlerTest {

  PaymentButtonClickHandler handler;
  @Mock
  DetailsPresenter mockPresenter;
  ArgumentCaptor<String> captor = null;
  @Before
  public void before() throws Exception{
    GWTMockUtilities.disarm();
    captor = ArgumentCaptor.forClass(String.class);
    handler = new PaymentButtonClickHandler(mockPresenter);
    mockStatic(Window.class);
    PowerMockito.doNothing().when(Window.class, "alert", captor.capture());
  }

  @After
  public void after(){
    GWTMockUtilities.restore();
  }

  @Test
  public void sanity() throws Exception {
    Window.alert("dd");
  }
}

Google provides the com.google.gwt.junit.GWTMockUtilities class to facilitate testing without launching any web server. This class provides methods for disabling and enabling GWT.create() behavior in isolation from the web server. The GWTMockUtilities.disarm() behavior replaces the normal GWT.create() behavior with a method that returns null instead of throwing a runtime exception. This is to allow JUnit tests to mock classes that make GWT.create() calls in their static initializers. GWTMockUtilities is not used with GWTTestCase and is not used to test widgets themselves. Rather, it is to allow pure Java unit tests of classes that need to manipulate widgets.

In our test, we need to create a mock button to represent the Payment button and disable and re-enable the button within our handler code. So, we need to disarm the static initialization.

The mockStatic(Window.class) class disables the Window.alert calls. The PowerMockito.doNothing().when(Window.class, "alert", captor.capture()) method is used to capture the arguments passed to the alert method. The following is the modified test with all the error conditions and happy path:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Window.class)
public class PaymentButtonClickHandlerTest {

  PaymentButtonClickHandler handler;
  @Mock
  DetailsPresenter mockPresenter;
  ArgumentCaptor<String> captor = null;
  @Mock
  ClickEvent clickEvent;
  @Mock
  DetailsView detailsView;
  @Mock
  HasValue<String> payAmount;
  @Before
  public void before() throws Exception{
    GWTMockUtilities.disarm();
    when(mockPresenter.getDetailsView()).thenReturn(detailsView);
    when(detailsView.getPaymentAmount()).thenReturn(payAmount);
    handler = new PaymentButtonClickHandler(mockPresenter);
    mockStatic(Window.class);
    captor = ArgumentCaptor.forClass(String.class);
    doNothing().when(Window.class, "alert", captor.capture());
  }

  @After
  public void after(){
    GWTMockUtilities.restore();
  }

  @Test
  public void when_empty_payment_amount_then_raises_error() {
    handler.onClick(clickEvent);
    assertEquals(PLEASE_ENTER_A_PAYMENT_AMOUNT,captor.getValue());
  }

  @Test
  public void when_invalid_payment_amount_then_raises_error() {
    when(payAmount.getValue()).thenReturn("abc$$$");
    handler.onClick(clickEvent);
    assertEquals(PLEASE_ENTER_A_VALID_PAYMENT_AMOUNT,captor.getValue());
  }

  @Test
  public void when_zero_payment_amount_then_raises_error(){
    when(payAmount.getValue()).thenReturn("0.00");
    handler.onClick(clickEvent);
    assertEquals(PLEASE_ENTER_A_POSITIVE_PAYMENT_AMOUNT,captor.getValue());
  }

  @Test
  public void when_negative_payment_amount_then_raises_error(){
    when(payAmount.getValue()).thenReturn("-10.00");
    handler.onClick(clickEvent);
    assertEquals(PLEASE_ENTER_A_POSITIVE_PAYMENT_AMOUNT,captor.getValue());
  }

  @Test
  public void when_payment_amount_exceeds_the_payable_then_raises_error(){
    when(payAmount.getValue()).thenReturn("100.00");
    when(detailsView.getOutstandingAmount()).thenReturn(new BigDecimal("50.00"));
    handler.onClick(clickEvent);
    assertEquals(PAYMENT_AMOUNT_CANNOT_EXCEED_THE_PAYABLE_AMOUNT,captor.getValue());
  }


  @Test
  public void when_payment_amount_not_greater_than_payable_amount_then_posts_the_payment() throws Exception {
    Button pay = PowerMockito.mock(Button.class);

    PowerMockito.when(clickEvent.getSource()).thenReturn(pay);
    when(payAmount.getValue()).thenReturn("100.00");
    when(detailsView.getOutstandingAmount()).thenReturn(new BigDecimal("200.00"));
    handler.onClick(clickEvent);
    verifyStatic(Mockito.never());
  }
}

So far, we covered the noninvasive, POJO-based Java unit tests for GWT, but Google provides a GWTTestCase class for invasive unit testing that acts as a bridge between the JUnit environment and the GWT environment. The GWTTestCase class extends the TestCase class. Running a compiled GWTTestCase subclass under JUnit launches the HtmlUnit browser, which serves to emulate your application behavior during test execution.

The typical way to set up a JUnit test case class is to have it extend TestCase and then run it with the JUnit TestRunner class. It is a convention to begin the name of all test methods with the prefix test. Now we use JUnit 4, which supports noninvasive, POJO-based unit testing and allows us to use annotations instead of conventions, such as a test should be started with the prefix test. So GWTTestCase is not recommended.

The HtmlUnit browser is an open source, GUI-less browser written in 100 percent Java. As HtmlUnit does not involve any native code, debugging GWT tests in development mode can be done entirely in a Java debugger. The HtmlUnit browser does not require firing up a new browser process; the HtmlUnit browser instances just run as new threads. To learn more about HtmlUnit or GWTTestCase, visit http://www.gwtproject.org/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.181.47