package edu.pdx.cs410J.gwt.client.mvp;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.rpc.AsyncCallback;
import edu.pdx.cs410J.gwt.client.DivisionServiceAsync;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.*;
/**
* Tests the functionality of the {@link DivisionPresenter}}
*
* @author David Whitlock
* @since Summer 2010
*/
public class DivisionPresenterTest {
/**
* Captures the handler passed to the view from the presenter
*/
@Captor
private ArgumentCaptor<ValueChangeHandler<String>> captor;
/**
* Captures the handler passed to the view from the presenter
*/
@Captor
private ArgumentCaptor<ValueChangeHandler<String>> captor2;
/**
* Captures the callback set to the division service
*/
@Captor
private ArgumentCaptor<AsyncCallback<Integer>> callbackCaptor;
private HandlerManager eventBus;
@Before
public void initializeMockito() {
MockitoAnnotations.initMocks(this);
}
@Before
public void initializeDependencies() {
eventBus = new HandlerManager(null);
}
/**
* Tests that the view is initialized with the appropriate values
*/
@Test
public void testInitialView() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
new DivisionPresenter(view, null, eventBus);
verify(view).setDivisionEnabled(false);
}
/**
* Tests that the view is notified when a valid dividend is provided
*/
@Test
public void testValidDividend() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
DivisionPresenter presenter = new DivisionPresenter(view, null, eventBus);
verify(view).setDividendChangeHandler(captor.capture());
int value = 123;
fireValueChangeEvent(String.valueOf(value), captor.getValue());
verify(view).setDividendValid(true);
verify(view, times(2)).setDivisionEnabled(false);
Integer dividend = presenter.getDividend();
assertNotNull(dividend);
assertEquals(value, dividend.intValue());
verify(view).setQuotient("");
}
/**
* Tests that the view is notified when an invalid dividend is provided
*/
@Test
public void testInvalidDividend() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
DivisionPresenter presenter = new DivisionPresenter(view, null, eventBus);
verify(view).setDividendChangeHandler(captor.capture());
fireValueChangeEvent("fred", captor.getValue());
verify(view).setDividendValid(false);
verify(view, times(2)).setDivisionEnabled(false);
assertNull(presenter.getDividend());
verify(view).setQuotient("");
}
/**
* Tests that the view is notified when a valid dividend is provided
*/
@Test
public void testValidDivisor() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
DivisionPresenter presenter = new DivisionPresenter(view, null, eventBus);
verify(view).setDivisorChangeHandler(captor.capture());
int value = 123;
fireValueChangeEvent(String.valueOf(value), captor.getValue());
verify(view).setDivisorValid(true);
verify(view, times(2)).setDivisionEnabled(false);
Integer dividend = presenter.getDivisor();
assertNotNull(dividend);
assertEquals(value, dividend.intValue());
verify(view).setQuotient("");
}
/**
* Tests that the view is notified when an invalid dividend is provided
*/
@Test
public void testInvalidDivisor() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
DivisionPresenter presenter = new DivisionPresenter(view, null, eventBus);
verify(view).setDivisorChangeHandler(captor.capture());
fireValueChangeEvent("fred", captor.getValue());
verify(view).setDivisorValid(false);
verify(view, times(2)).setDivisionEnabled(false);
assertNull(presenter.getDivisor());
verify(view).setQuotient("");
}
/**
* Tests that a valid dividend and divisor enables division
*/
@Test
public void testValidDivisorAndDividend() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
new DivisionPresenter(view, null, eventBus);
verify(view).setDivisorChangeHandler(captor.capture());
verify(view).setDividendChangeHandler(captor2.capture());
fireValueChangeEvent(String.valueOf(123), captor.getValue());
fireValueChangeEvent(String.valueOf(234), captor2.getValue());
verify(view).setDivisionEnabled(true);
verify(view, times(2)).setQuotient("");
}
/**
* Tests that the division service is invoked appropriately
*/
@Test
public void testServiceInvocation() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
DivisionServiceAsync service = mock(DivisionServiceAsync.class);
DivisionPresenter presenter = new DivisionPresenter(view, service, eventBus);
ArgumentCaptor<ClickHandler> captor = ArgumentCaptor.forClass(ClickHandler.class);
verify(view).setDivisionClickHandler(captor.capture());
int divisor = 123;
presenter.setDivisor(divisor);
int dividend = 234;
presenter.setDividend(dividend);
fireClickEvent(captor.getValue());
verify(service).divide(eq(dividend), eq(divisor), any(AsyncCallback.class));
}
/**
* Tests that the quotient is set appropriately
*/
@Test
public void testQuotientIsSet() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
DivisionServiceAsync service = mock(DivisionServiceAsync.class);
DivisionPresenter presenter = new DivisionPresenter(view, service, eventBus);
ArgumentCaptor<ClickHandler> clickCaptor = ArgumentCaptor.forClass(ClickHandler.class);
verify(view).setDivisionClickHandler(clickCaptor.capture());
presenter.setDivisor(123);
presenter.setDividend(234);
fireClickEvent(clickCaptor.getValue());
verify(service).divide(anyInt(), anyInt(), callbackCaptor.capture());
Integer quotient = 456;
callbackCaptor.getValue().onSuccess(quotient);
verify(view).setQuotient(String.valueOf(quotient));
verify(view, never()).setErrorMessage(any(String.class));
}
/**
* Tests that the error message is set appropriately when the remote service fails
*/
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
@Test
public void testErrorMessageIsSet() {
DivisionPresenter.Display view = mock(DivisionPresenter.Display.class);
DivisionServiceAsync service = mock(DivisionServiceAsync.class);
DivisionPresenter presenter = new DivisionPresenter(view, service, eventBus);
ArgumentCaptor<ClickHandler> clickCaptor = ArgumentCaptor.forClass(ClickHandler.class);
verify(view).setDivisionClickHandler(clickCaptor.capture());
presenter.setDivisor(123);
presenter.setDividend(234);
fireClickEvent(clickCaptor.getValue());
verify(service).divide(anyInt(), anyInt(), callbackCaptor.capture());
final String message = "This is an error message";
callbackCaptor.getValue().onFailure(new RuntimeException() {
@Override
public String toString() {
return message;
}
});
verify(view, never()).setQuotient(any(String.class));
verify(view).setErrorMessage(message);
}
/**
* Fires a {@link ClickEvent} to a given handler
*
* @param handler The handler that wlll receive the event
*/
private void fireClickEvent(ClickHandler handler) {
// Firing a ClickEvent requires a NativeEvent which is only available with GWT.create(), not in JUnit tests
handler.onClick(mock(ClickEvent.class));
}
/**
* Fires a {@link ValueChangeEvent} to a given handler
*
* @param value The value that is changing
* @param handler The handler that will receive the event
*/
private <T> void fireValueChangeEvent(T value, ValueChangeHandler<T> handler) {
HasValueChangeHandlers<T> mock = new MockHasValueChangeHandlers<T>();
mock.addValueChangeHandler(handler);
ValueChangeEvent.fire(mock, value);
}
private abstract class MockHasHandlers implements HasHandlers {
protected final HandlerManager manager;
private MockHasHandlers() {
manager = new HandlerManager(this);
}
public void fireEvent(GwtEvent<?> event) {
manager.fireEvent(event);
}
}
/**
* A mock that takes care of the handling and firing events
*
* @param <T>
*/
private class MockHasValueChangeHandlers<T> extends MockHasHandlers implements HasValueChangeHandlers<T> {
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<T> handler) {
return manager.addHandler(ValueChangeEvent.getType(), handler);
}
}
}