Напишите тест Mockito для класса Presenter (шаблон First Presenter)
Я пытаюсь познакомиться с TDD и Presenter First Pattern. Прямо сейчас я застрял в написании тестового примера для моего Presenter.class. Моя цель - охватить весь Presenter.class, включая Action Event, но я не знаю, как это сделать с помощью Mockito.
Presenter.class:
public class Presenter {
IModel model;
IView view;
public Presenter(final IModel model, final IView view) {
this.model = model;
this.view = view;
this.model.addModelChangesListener(new AbstractAction() {
public void actionPerformed(ActionEvent arg0) {
view.setText(model.getText());
}
});
}}
IView.class:
public interface IView {
public void setText(String text);
}
IModel.class:
public interface IModel {
public void setText();
public String getText();
public void whenModelChanges();
public void addModelChangesListener(AbstractAction action);
}
PresenterTest.class:
@RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
@Mock
IView view;
@Mock
IModel model;
@Before
public void setup() {
new Presenter(model, view);
}
@Test
public void test1() {
}
}
Заранее спасибо!
3 ответа
Сначала... спасибо, ребята!
Через некоторое время я разобрался с этим решением и придерживался его, потому что я не хотел реализовывать какие-либо интерфейсы в классе презентатора, а также не хотел создавать классы-заглушки в своих тестах.
IView
public interface IView {
public void setText(String text);
}
IModel
public interface IModel {
public String getText();
public void addModelChangeListener(Action a);
}
Ведущий
public class Presenter {
private IModel model;
private IView view;
public Presenter(final IModel model, final IView view) {
this.model = model;
this.view = view;
model.addModelChangeListener(new AbstractAction() {
public void actionPerformed(ActionEvent e) {
view.setText(model.getText());
}
});
}
}
PresenterTest
@RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
@Mock
IView view;
@Mock
IModel model;
@Test
public void when_model_changes_presenter_should_update_view() {
ArgumentCaptor<Action> event = ArgumentCaptor.forClass(Action.class);
when(model.getText()).thenReturn("test-string");
new Presenter(model, view);
verify(model).addModelChangeListener(event.capture());
event.getValue().actionPerformed(null);
verify(view).setText("test-string");
}
}
В этой ситуации связь между моделью и докладчиком достаточно свободна (общение через слушателя действия), поэтому вам, вероятно, лучше не использовать макет для модели.
Вы можете использовать реальную модель (я бы предпочел, чтобы реальная модель была достаточно простой), или, как я сделал в приведенном ниже фрагменте кода, сделать заглушку внутри своего тестового кода.
@RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
@Mock
IView view;
IModel model;
@Before
public void setup() {
model = new StubModel();
new Presenter(model, view);
}
@Test
public void presenterUpdatesViewWhenModelChanges() {
model.setText("Test Text");
verify(view).setText("Test Text");
}
private class StubModel implements IModel {
private String text;
private List<ActionListener> actionListeners;
StubModel() {
actionListeners = new ArrayList<ActionListener>();
}
@Override
public void setText(String text) {
this.text = text;
whenModelChanges();
}
@Override
public String getText() {
return text;
}
@Override
public void whenModelChanges() {
for (ActionListener listener: actionListeners) {
listener.actionPerformed(null);
}
}
@Override
public void addModelChangesListener(AbstractAction action) {
actionListeners.add(action);
}
}
}
Возможно, вы могли бы настроить этот тест с помощью фиктивной модели, в которой вы настраиваете вызовы-заглушки, но чтобы сделать это разумно, вам также может понадобиться фиктивное действие, которое усложнит ситуацию, так как действие создается докладчиком.
Это кажется большим количеством тестового кода для тестирования по существу одной строки кода в вашем классе презентатора, но самый большой кусок - это модель-заглушка, которая может быть заменена реальной моделью или извлечена из этого класса тестирования и использоваться совместно с другими тестами.
Это тот случай, когда небольшой рефакторинг может иметь большое значение. Научитесь "слушать" тесты и пусть они управляют дизайном. Model
нужно только знать, что ActionListener
должен быть уведомлен, ему все равно, является ли это AbstractAction
, Используйте наименьший возможный интерфейс в ваших классовых контрактах. Вот рефакторинг, чтобы сделать простой тест (возможно, слишком простой, чтобы стоить модульного тестирования, но вы поняли идею):
Presenter.class:
public class Presenter {
public Presenter(final IModel model, final IView v) implements ActionListener {
this.model = model;
this.view = v;
model.addModelChangesListener(this);
}
public void actionPerformed(ActionEvent arg0) {
view.setText(model.getText());
}
}
IModel.class:
public interface IModel {
public void addModelChangesListener(ActionListener action);
}
PresenterTest.class:
@RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
@Mock IView view;
@Mock IModel model;
@Test
public void when_model_changes_presenter_should_update_text() {
when(model.getText()).thenReturn("Test Text");
Presenter p = new Presenter(model, view);
p.actionPerformed(null);
verify(view).setText("Test Text");
}
}