如何使用Vaadin进行测试驱动开发?

12
什么是最好的构建Vaadin应用程序的方式,以便我可以使用TDD(测试驱动开发)来创建应用程序?换句话说,我不想编写需要服务器、浏览器(甚至是这些的模拟器)的测试,因为它们可能太脆弱、太慢或两者兼而有之。
问题将GWT MVP模式翻译为Vaadin有些相关,因为我正在寻找正确的模式,使我的UI“逻辑”尽可能可测试,但我不确定MVP是否适用于Vaadin的世界。
5个回答

4

看看模型-视图-控制器(Model View Presenter)模式,也被称为“谦逊的视图”。

如果做得正确,View是唯一无法测试的对象,因为它不包含任何逻辑,你可以简单地不必测试它。


2
有一篇很好的文章描述了在 Vaadin 中使用 MVP 设计模式。https://vaadin.com/web/magi/home/-/blogs/model-view-presenter-pattern-with-vaadin - Vojtech Ruzicka

3
我刚开始使用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事件监听器等功能(您会注意到按钮有标题但没有操作监听器)。但是我正在不断进步!


1
你可以使用karibu-testing框架:https://github.com/mvysny/karibu-testing。它允许您使用实际的完整Vaadin组件运行和测试应用程序,因此您不必使用MVP或自定义组件工厂来构建特殊的UI进行测试。请参见上面的链接,了解有关如何为您的应用程序编写和运行Vaadin单元测试的具体示例和教程。

1

模型视图控制器(MVP)模式是划分Vaadin应用程序的表现逻辑的良好且推荐的方式。它甚至是官方高级Vaadin培训课程的一部分。这里是Vaadin中MVP实现的示例。但是,根据具体应用程序的不同,可以使用各种版本的MVP。

理想状态是可测试的Presenter包含尽可能多的逻辑,而视图则尽可能被动。对于实际视图的测试,最好使用Web测试从用户的角度进行测试。Vaadin提供了一个特殊的工具Vaadin TestBench,它基于Selenium Webdriver并修改为Vaadin环境。TestBench比普通的Selenium具有一些优势,如Vaadin调整等待Ajax操作和高级截图比较。这应该与某种Selenium Grid结合使用,例如SauceLabs,它提供了各种操作系统、Web浏览器及其版本的组合,可以用于您的测试。
请注意,Vaadin TestBench不是免费的,需要许可证或Vaadin专业订阅

1

一旦Vaadin是基于UI的Web框架,您可以选择基于验收测试(如Selenium)的测试解决方案。因此,您仍然可以在业务/模型层中使用测试驱动开发,该层应完全与UI类隔离。

UI是您可以触摸的东西,您可以更改它并在修改时立即查看,您可以实时接受行为,并使用一些好的工具自动化它。

业务/模型是关键层,您需要改进API设计以便理解和将业务转换为代码。对于任何更改,您需要确保其不会违反规则-为此,只需使用单元测试(TDD在这里完全适用,但不是强制性的)


谢谢回复,但我正在寻找更具体的模式或特定的实践方法。你的答案相当模糊和笼统,所以对我来说并没有什么帮助。 - Ted M. Young

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接