从单元测试的角度来看:视图应该指定演示者还是 GWT MVP 中的其他方式?

Posted

技术标签:

【中文标题】从单元测试的角度来看:视图应该指定演示者还是 GWT MVP 中的其他方式?【英文标题】:In a unit test perspective: should the view specify presenter or the other way around in GWT MVP? 【发布时间】:2011-08-16 04:28:04 【问题描述】:

在有关 GWT 的教程中,Google 使用不同的方法来执行 MVP,View 指定 Presenter 或 Presenter 指定 View。第一个在使用活动和地点时使用。

这篇文章涉及到这个主题:MVP: Should the View implement a Presenter's interface or vice versa?

但是,我想知道在对演示者和视图进行单元测试时,您认为哪种方案最好?还是两者都一样好用?

【问题讨论】:

【参考方案1】:

presenter 单元测试背后的想法是模拟视图,并针对该模拟视图的状态编写几个断言,这些断言将在现实生活中的应用程序中以视觉方式表示。多亏了这种方法,不需要运行完整的GWTTestCase,这会花费大量时间,应该将其归入集成测试,而不是单元测试。

如果您同时尝试这两种 MVP 方法,单元测试可能如下所示:

MVP 1:

@Test
public void shouldAddContactOnAddContactClicked() 
    // given
    ContactsPresenter.Display display = mock(ContactsPresenter.Display.class);
    MockButton addButton = new MockButton();
    given(display.getAddButton()).willReturn(addButton);
    ContactsDisplay.Presenter presenter = new ContactsPresenter();
    presenter.bindView(display);
    presenter.setContacts(new ArrayList<Contact>());

    // when
    addButton.click();

    // then
    verify(display).addContact(any());
    assertThat(presenter.getContacts().size(), is(1));

MockButton 是我在这里描述的: Comprehensive Pros/Cons of Mocking Frameworks for GWT

虽然可能,但以这种方式模拟事物并不是很方便。 MVP2 方法似乎表现更好:

@Test
public void shouldAddContactOnAddContactClicked() 
    // given
    ContactsView view = mock(ContactsView.class);
    ContactsView.Presenter presenter = new ContactsPresenter();
    presenter.bindView(view); // assuming that presenter will call view.setPresenter(this)
    presenter.setContacts(new ArrayList<Contact>());

    // when
    presenter.onAddContactClicked();

    // then
    verify(view).addContact(any()); 
    assertThat(presenter.getContacts().size(), is(1));

使用第二种方法的另一个原因是 MVP1 中声明显示元素的问题,这会触发不同的事件(例如 ClickEvent、FocusEvent)。当涉及到UiBinder 时,MVP2 也让事情变得更容易。

【讨论】:

非常感谢!您能否更详细地描述您在最后一句中关于在“MVP1”中声明用于触发事件的显示元素的问题以及 UiBinder 如何更适合“MVP2”? 想象Display,你有getAddButton()方法,它应该返回实现HasClickHandlersHasAllTouchHandlers的类型。可以使用像&lt;T extends HasClickHandelrs &amp; HasAllTouchHandlers&gt; 这样的泛型,但后来发现它非常不方便,而且并不比显式转换更好。我们还可以定义额外的getAddButtonTouch() 方法,但是这样的方法对我来说似乎很违反直觉。 所以你说的基本上是,如果 GUI 元素有多个目的并且应该在 Presenter 中触发不同的操作,那么在 View 中使用委托给不同方法的 UiHandler 会更容易、更清晰在演示者中? UiBinder: 实际上它引入了很多逻辑,否则会被放到presenter中,特别是如果你使用编辑器和驱动程序。我将代码切换到 MVP2 并使用 UiHandler 将事件“推送”到演示者中,而不是在演示者中拉取事件处理程序。它节省了一些样板代码,并且正如 Thomas 建议的那样,您可以重用视图,这在 MVP1 中是相当困难的。注意:我在视图中创建驱动程序和编辑器,然后将它们公开给演示者。 谢谢。您对 MVP2 是否真的是 MVP 有意见,或者它是否通过让 View 了解 Presenter 并让 View 指定 Presenter 来打破某些“合同”,Shahzeb 指出***.com/questions/3309029/… 的答案是? 【参考方案2】:

避免使用HasXxxHandlers,即使用part 2 文章中的方法,其中每个对等点都有对另一个的引用。 HasXxxHandlers 太复杂而无法模拟,尤其是在使用 Mockito 或 EasyMock 等模拟库时。 为了获得最佳可测试性,您将视图注入到演示者中,然后演示者将调用视图的 setPresentersetDelegate 方法(这样,您可以在正确的时间对您正确调用该方法进行单元测试)。使用活动时,您的活动是演示者,您可能会在startview.setPresenter(null) 中调用view.setPresenter(this) onStoponCancel;这样一来,您就可以与多个演示者共享一个单例视图(但显然,一次最多只能有一个)。

【讨论】:

非常感谢!当您说 HasXxxHandlers 太复杂而无法模拟时,您能否更详细地描述一下您的意思? 使用HasClickHandlers getSaveButton() 模拟视图涉及:模拟HasClickHandlers,它应该捕获ClickHandler 作为addClickHandler 的参数;如果您删除了您的处理程序,那么您将不得不从您的模拟 addClickHandler 返回一个新的 HandlerRegistration 并在其 removeHandler 方法上添加适当的 assert,这意味着保留对它的引用在您的测试代码中。触发事件意味着嘲笑ClickEvent。相比之下,模拟一个视图,该视图捕获传递给其设置器的委托/演示者并在其上调用 saveClick【参考方案3】:

在单元测试方面,这真的没关系。如果你设计得当,这只是你需要在哪里注入存根(或模拟)的问题。不应允许单元测试决定设计决策。但是,单元测试通常会指示设计是否错误(例如缺少注入等)

【讨论】:

以上是关于从单元测试的角度来看:视图应该指定演示者还是 GWT MVP 中的其他方式?的主要内容,如果未能解决你的问题,请参考以下文章

4.4 Go语言中的单元测试

4.4 Go语言中的单元测试

在 MVP 模式中,适配器应该持有模型还是演示者应该持有模型并让适配器引用它?

在带有选项卡的 Winforms 的模型视图演示器中应该使用多少个演示器?

我应该以MVP模式将我的视图投射到iOS上的演示者中的UIViewController吗?

在 Swift 中对符合协议的对象和变量进行单元测试