5

我已经完成了我的大部分 GWT 测试 MVP 风格,没有测试小部件。我希望能够构建更复杂的小部件并在不使用GwtTestCase(慢)的情况下很好地测试它们。

出于好奇,我尝试了一个非常简单的测试。给定一个非常简单的小部件,有点像这样(这不是我的确切类,只是一个简化的示例):

public class MyWidget extends Composite {
    private TextBox boxOne, boxTwo;

    public MyWidget() {
        boxOne = new TextBox();
        boxTwo = new TextBox();
        VerticalPanel panel = new VerticalPanel();
        panel.add( boxOne );
        panel.add( boxTwo );
        initWidget( panel );
    }

    public String[] getText() {
      return new String[] { boxOne.getText(), boxTwo.getText() }
    }
}

我正在使用一个 GWTMockito 测试,有点像这样:

public class MyWidgetTest {

    private ConstantsWithLookup constants;
    private MyWidget widget;

    @Before
    public void createMocks() {
        GwtMockito.initMocks( this );
        constants = mock( ConstantsWithLookup.class );
    }

    @Test
    public void testIsInvalidByDefault() {
        widget = new MyWidget( constants ) {
            protected void initWidget(Widget w) {
                // Disarm for testing
              }
        };
        assertNotNull( widget );
    }

    @After
    public void tearDown() {
        GwtMockito.tearDown();
    }

}

我立即得到:

java.lang.UnsatisfiedLinkError: com.google.gwt.dom.client.Document.nativeGet()Lcom/google/gwt/dom/client/Document;
    at com.google.gwt.dom.client.Document.nativeGet(Native Method)
    at com.google.gwt.dom.client.Document.get(Document.java:46)
    at com.google.gwt.user.client.ui.TextBox.<init>(TextBox.java:78)
    at mypackage.MyWidget.<init>(MyWidget.java:linenumber)
    ... etc ...

你会注意到我没有使用测试运行器——我尝试过,但我正在测试它的项目使用的是 JUnit 4.4,测试运行器似乎不适用于 JUnit 4.4。如果我使用 GwtMockitoTestRunner,我会得到:

java.lang.NoSuchFieldError: NULL
    at org.junit.runners.ParentRunner.<init>(ParentRunner.java:57)
    at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:57)
    at com.google.gwtmockito.GwtMockitoTestRunner.<init>(GwtMockitoTestRunner.java:114)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

基于另一个堆栈溢出问题和快速调查,我倾向于认为这与该项目中使用的 JUnit 版本有关。GwtMockito 使用BlockJunit4ClassRunner,标记为@since 4.5。

那么——这个测试是 GWTMockito 应该帮助的吗?我对 GWTMockito 完全陌生,所以这很容易成为一个简单的误解,但我想理解。

Composite我可以使用 GWTMockito 来测试使用/由较小的小部件构建的复杂小部件IsWidget吗?GWTMockito 不应该帮助我解决这个 JSNI 问题,还是我误读了什么?仅仅是因为我没有使用测试运行器吗?

4

1 回答 1

5

在我看来,有两种可能:

  1. 您忘记使用 GwtMockito 的 JUnit 运行器:

    @RunWith(GwtMockitoTestRunner.class)
    public class MyTest {
        // ...
    }
    

    如果您需要自定义运行器来做其他事情,您可以用另一种方式设置 GwtMockito 。

  2. 您不GWT.create用于实例化您的小部件。

第一点很简单——这个跑步者是必需的,这样 GwtMockito 才能发挥它的“魔力”。

第二点需要解释一下:GwtMockito 使用 GWT 的Deferred Binding工作。这意味着,您希望 GwtMockito 自动模拟的所有小部件都必须通过调用GWT.create. 这对于 UiBinder 来说很简单——在内部,在 UiBinder 的模板中定义的所有小部件都使用 实例化GWT.create,因此您无需更改任何内容即可将其与 GwtMockito 一起使用。但是,如果您没有使用 UiBinder(或者是providing您自己的小部件实例),GwtMockito 将无法发挥它的魔力,除非您使用GWT.create.

来自GwtMockito 的文档(强调我的):

GwtMockito 通过允许您从 JUnit 测试调用GWT.create并返回 Mockito mocks来解决这个和其他与 GWT 相关的测试问题。


更新

我可以验证使用 JUnit 4.4,跑步者抛出了你提到的异常。为此,我在 GwtMockito 的跟踪器上打开了一个问题

至于测试本身,我已经设法让它发挥作用。正如我之前提到的,GwtMockito 的工作要归功于 Deferred Binding。这意味着,您的小部件也必须用GWT.create成员实例化。这就是 GwtMockito 进入小部件的“方式”。如果您只是调用new TextBox()它,它将无法用模拟代替它。
如果您将 MyWidget 类更改为以下内容,它将通过测试(注意对 的调用GWT.create)。

public class MyWidget extends Composite {
    private TextBox boxOne, boxTwo;

    public MyWidget() {
        boxOne = GWT.create(TextBox.class);
        boxTwo = GWT.create(TextBox.class);
        VerticalPanel panel = GWT.create(VerticalPanel.class);
        panel.add( boxOne );
        panel.add( boxTwo );
        initWidget( panel );
    }

    public String[] getText() {
        return new String[] { boxOne.getText(), boxTwo.getText() };
    }
}

知道了这一点,有一些选择:

  • 永远记得用GWT.create
  • 将它们公开为包可见(您在 UiBinder 中执行的方式)并在您的测试中为它们分配模拟(从mock或创建)GWT.create
  • 切换到 UiBinder ;/

没有一个看起来太有吸引力,第一个似乎是最好的。

于 2014-10-12T11:04:45.837 回答