Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GWTMockito UnsatisfiedLinkError

I've done most of my GWT testing MVP-style, without testing widgets. I'd like to be able to build more complex widgets and test them well without using GwtTestCase (slow).

Out of curiosity, I tried an extremely simple test. Given a very simple widget a little like this (this isn't my exact class, just a simplified example):

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() }
    }
}

And I'm using a GWTMockito test a little like this:

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();
    }

}

I immediately get:

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 ...

You'll note I'm not using the test runner -- I tried to, but the project I'm testing this on is using JUnit 4.4, the test runner doesn't seem to work with JUnit 4.4. If I use the GwtMockitoTestRunner, I get:

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)

Based on another stack overflow question, and a quick investigation, I'm inclined to believe this has to do with the version of JUnit in use on this project. GwtMockito uses BlockJunit4ClassRunner, which is marked @since 4.5.

So -- is this test the sort of thing that GWTMockito is supposed to help with? I'm totally new to GWTMockito, so it could easily be a simple misunderstanding, but I'd like to understand.

Can I use GWTMockito to test complex widgets that are built from smaller widgets using Composite / IsWidget? Shouldn't GWTMockito have helped me with this JSNI issue, or am I misreading something? Is it just because I'm not using the test runner?

like image 807
Geoffrey Wiseman Avatar asked Oct 10 '14 22:10

Geoffrey Wiseman


1 Answers

The way I see it, there are two possibilities:

  1. You forgot to use the GwtMockito's JUnit runner:

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

    You can setup GwtMockito in an alternative way, if you need a custom runner for something else.

  2. You are not using GWT.create to instantiate your widgets.

The first point is straightforward - this runner is required so that GwtMockito can do its "magic".

The second point requires an explanation: GwtMockito works by using GWT's Deferred Binding. Meaning, all the widgets you want automatically mocked by GwtMockito must be instantiated by a call to GWT.create. This is straightforward with UiBinder - internally, all the widgets defined in the UiBinder's template are instantiated with GWT.create, so you don't have to change anything to use it with GwtMockito. But if you aren't using UiBinder (or are providing your own instances of widgets), GwtMockito won't be able to work its magic unless you instantiate your widgets with GWT.create.

From GwtMockito's documentation (emphasis mine):

GwtMockito solves this and other GWT-related testing problems by allowing you to call GWT.create from JUnit tests, returning Mockito mocks.


Update

I can verify that with JUnit 4.4, the runner is throwning the exception you mentioned. I've opened an issue on GwtMockito's tracker for this.

As for the test itself, I've managed to get it to work. As I mentioned before, GwtMockito works thanks to Deferred Binding. Meaning, your widgets have to be instantiated with GWT.create - the members too. That's GwtMockito's "way in" into your widget. If you just call new TextBox() it won't be able to substitute that with a mock.
If you change your MyWidget class to the following it will pass the test (notice the calls to 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() };
    }
}

Knowing this, there are some options:

  • always remember to instantiate all widgets in your composites with GWT.create
  • expose them as package visible (the way you do in UiBinder) and assign them mocks (either created from mock or GWT.create) in your tests
  • switch to UiBinder ;/

None seem too attractive, the first one seems the best in general.

like image 183
Igor Klimer Avatar answered Sep 21 '22 16:09

Igor Klimer