【发布时间】:2011-11-23 04:44:42
【问题描述】:
构建基于 Vaadin 的应用程序以便我可以使用 TDD(测试驱动开发)来创建应用程序的最佳方法是什么?换句话说,我不想编写需要服务器或浏览器(甚至它们的模拟器)的测试,因为它们可能太脆弱、太慢或两者兼而有之。
Translating GWT MVP Pattern to Vaadin 的问题有点相关,因为我正在寻找正确的模式以使我的 UI“逻辑”尽可能可测试,但我不确定 MVP 是否适用于 Vaadin。
【问题讨论】:
构建基于 Vaadin 的应用程序以便我可以使用 TDD(测试驱动开发)来创建应用程序的最佳方法是什么?换句话说,我不想编写需要服务器或浏览器(甚至它们的模拟器)的测试,因为它们可能太脆弱、太慢或两者兼而有之。
Translating GWT MVP Pattern to Vaadin 的问题有点相关,因为我正在寻找正确的模式以使我的 UI“逻辑”尽可能可测试,但我不确定 MVP 是否适用于 Vaadin。
【问题讨论】:
查看Model View Presenter 模式,也称为“谦虚视图”。
如果操作正确,视图是唯一您无法测试的对象,并且由于它不包含任何逻辑,您根本不必费心对其进行测试。
【讨论】:
我才刚刚开始使用 Vaadin,并且“我可以使用 Vaadin 进行 TDD 吗?”是我首先考虑的。我发现(到目前为止)实际上很容易。虽然我最终写了很多代码。
我要做的第一件事就是编写一些工厂类。这样我就可以将模拟 UI 对象注入到我的类中。例如:
public class ButtonFactory {
public Button create() {
return new Button();
}
public Button create(String caption) {
return new Button(caption);
}
public Button create(String caption, Button.ClickListener listener) {
return new Button(caption, listener);
}
}
然后我为我需要的主要 UI 组件创建了工厂:
@ApplicationScoped
public class SiteAdminButtonBarFactory implements Serializable {
private static final long serialVersionUID = -462493589568567794L;
private ButtonFactory buttonFactory = null;
private HorizontalLayoutFactory horizontalLayoutFactory = null;
public SiteAdminButtonBarFactory() {}
@Inject
public SiteAdminButtonBarFactory(HorizontalLayoutFactory horizontalLayoutFactory, ButtonFactory buttonFactory) {
this.horizontalLayoutFactory = horizontalLayoutFactory;
this.buttonFactory = buttonFactory;
}
public SiteAdminButtonBar create() {
HorizontalLayout layout = horizontalLayoutFactory.create();
layout.addComponent(addButton());
layout.addComponent(removeButton());
layout.addComponent(editButton());
return new SiteAdminButtonBar(layout);
}
private Button addButton() {
return buttonFactory.create("Add");
}
private Button removeButton() {
return buttonFactory.create("Remove");
}
private Button editButton() {
return buttonFactory.create("Edit");
}
}
相关的测试代码是:
public class SiteAdminButtonBarFactoryTest {
private HorizontalLayout horizontalLayout = null;
private HorizontalLayoutFactory horizontalLayoutFactory = null;
private Button addButton = null;
private Button removeButton = null;
private Button editButton = null;
private ButtonFactory buttonFactory = null;
private SiteAdminButtonBarFactory siteAdminButtonBarFactory = null;
@Test
public void shouldCreateAHorizontalLayout() throws Exception {
givenWeHaveAFullyConfiguredSiteAdminButtonBarFactory();
SiteAdminButtonBar siteAdminButtonBar = siteAdminButtonBarFactory.create();
assertThat(siteAdminButtonBar, is(notNullValue()));
verify(horizontalLayoutFactory).create();
}
@Test
public void shouldContainAnADDButton() throws Exception {
givenWeHaveAFullyConfiguredSiteAdminButtonBarFactory();
siteAdminButtonBarFactory.create();
verify(buttonFactory).create("Remove");
verify(horizontalLayout).addComponent(removeButton);
}
@Test
public void shouldContainARemoveButton() throws Exception {
givenWeHaveAFullyConfiguredSiteAdminButtonBarFactory();
siteAdminButtonBarFactory.create();
verify(buttonFactory).create("Edit");
verify(horizontalLayout).addComponent(editButton);
}
@Test
public void shouldContainAnEditButton() throws Exception {
givenWeHaveAFullyConfiguredSiteAdminButtonBarFactory();
siteAdminButtonBarFactory.create();
verify(buttonFactory).create("Add");
verify(horizontalLayout).addComponent(addButton);
}
private void givenWeHaveAFullyConfiguredSiteAdminButtonBarFactory() {
horizontalLayout = mock(HorizontalLayout.class);
horizontalLayoutFactory = mock(HorizontalLayoutFactory.class);
when(horizontalLayoutFactory.create()).thenReturn(horizontalLayout);
addButton = mock(Button.class);
removeButton = mock(Button.class);
editButton = mock(Button.class);
buttonFactory = mock(ButtonFactory.class);
when(buttonFactory.create("Add")).thenReturn(addButton);
when(buttonFactory.create("Remove")).thenReturn(removeButton);
when(buttonFactory.create("Edit")).thenReturn(editButton);
siteAdminButtonBarFactory = new SiteAdminButtonBarFactory(horizontalLayoutFactory, buttonFactory);
}
}
我承认一开始我必须先编写代码,然后再进行测试,直到我弄清楚如何构建事物。此外,我还没有对事件监听器等进行 TDD(你会注意到按钮有标题,但没有动作监听器)。但我快到了!
【讨论】:
一旦 Vaadin 是基于 UI 的 Web 框架,您就可以选择基于验收测试的测试解决方案,例如 Selenium。因此,您仍然可以在您的业务/模型层中使用测试驱动开发,这应该与您的 UI 类完全隔离。
UI 是您可以触摸的东西,您可以更改它并在修改的那一刻看到它,您可以实时接受行为并使用一些好的工具将其自动化。
业务/模型是一个关键层,您需要改进 API 设计以便更好地理解并将业务转换为代码。对于任何更改,您都需要确保安全,不要违反您的规则 - 为此,只需使用单元测试(此处完全应用 TDD,但不是强制性的)
【讨论】:
Model View Presenter 模式实际上是划分 Vaadin 应用程序表示逻辑的好方法和推荐方法。它甚至是官方Advanced Vaadin Training 课程的一部分。 Here is the example 在 Vaadin 中实现 MVP。但是,根据具体应用,可以使用各种版本的 MVP。
理想状态是可测试的presenter包含尽可能多的逻辑,视图尽可能被动。对于实际视图的测试,最好使用Web Tests从用户的角度进行测试。 Vaadin 为此提供了一个特殊的工具 - Vaadin TestBench,它基于 Selenium Webdriver 并修改为 Vaadin Environment。 TestBench 有some advantages 提供普通硒,例如 vaadin 调整等待 ajax 动作和高级屏幕截图比较。这应该与某种 Selenium Grid 结合使用,例如 SauceLabs,它提供了广泛的操作系统、Web 浏览器及其版本的组合,可用于您的测试。
请注意,Vaadin TestBench 不是免费的,需要许可证或Vaadin pro subscription。
【讨论】:
您可以使用 karibu 测试框架:https://github.com/mvysny/karibu-testing。它允许您使用实际成熟的 Vaadin 组件运行和测试您的应用程序,因此您不必使用 MVP 或自定义组件工厂来构建特殊的 UI 进行测试。请参阅上面的链接,了解如何为您的应用编写和运行 Vaadin 单元测试的具体示例和教程。
【讨论】: