【问题标题】:In a unit test perspective: should the view specify presenter or the other way around in GWT MVP?从单元测试的角度来看:视图应该指定演示者还是 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?

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

【问题讨论】:

    标签: unit-testing gwt view mvp presenter


    【解决方案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()方法,它应该返回实现HasClickHandlers和HasAllTouchHandlers的类型。可以使用像&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 指出stackoverflow.com/questions/3309029/… 的答案是?
    【解决方案2】:

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

    【讨论】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-01
      • 2016-11-15
      • 1970-01-01
      • 2019-09-27
      • 1970-01-01
      • 2019-11-18
      相关资源
      最近更新 更多