从单元测试的角度来看:视图应该指定演示者还是 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()
方法,它应该返回实现HasClickHandlers
和HasAllTouchHandlers
的类型。可以使用像<T extends HasClickHandelrs & HasAllTouchHandlers>
这样的泛型,但后来发现它非常不方便,而且并不比显式转换更好。我们还可以定义额外的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 等模拟库时。
为了获得最佳可测试性,您将视图注入到演示者中,然后演示者将调用视图的 setPresenter
或 setDelegate
方法(这样,您可以在正确的时间对您正确调用该方法进行单元测试)。使用活动时,您的活动是演示者,您可能会在start
和view.setPresenter(null)
中调用view.setPresenter(this)
onStop
和onCancel
;这样一来,您就可以与多个演示者共享一个单例视图(但显然,一次最多只能有一个)。
【讨论】:
非常感谢!当您说 HasXxxHandlers 太复杂而无法模拟时,您能否更详细地描述一下您的意思? 使用HasClickHandlers getSaveButton()
模拟视图涉及:模拟HasClickHandlers
,它应该捕获ClickHandler
作为addClickHandler
的参数;如果您删除了您的处理程序,那么您将不得不从您的模拟 addClickHandler
返回一个新的 HandlerRegistration
并在其 removeHandler
方法上添加适当的 assert
,这意味着保留对它的引用在您的测试代码中。触发事件意味着嘲笑ClickEvent
。相比之下,模拟一个视图,该视图捕获传递给其设置器的委托/演示者并在其上调用 saveClick
。【参考方案3】:
在单元测试方面,这真的没关系。如果你设计得当,这只是你需要在哪里注入存根(或模拟)的问题。不应允许单元测试决定设计决策。但是,单元测试通常会指示设计是否错误(例如缺少注入等)
【讨论】:
以上是关于从单元测试的角度来看:视图应该指定演示者还是 GWT MVP 中的其他方式?的主要内容,如果未能解决你的问题,请参考以下文章
在 MVP 模式中,适配器应该持有模型还是演示者应该持有模型并让适配器引用它?
在带有选项卡的 Winforms 的模型视图演示器中应该使用多少个演示器?