Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocked @UiField types are erased, causing ClassCastExceptions #50

Open
cushon opened this issue Feb 12, 2015 · 1 comment
Open

Mocked @UiField types are erased, causing ClassCastExceptions #50

cushon opened this issue Feb 12, 2015 · 1 comment

Comments

@cushon
Copy link
Contributor

cushon commented Feb 12, 2015

The implementation of FakeUiBinderProvider erases the type of all of the fields it injects (e.g. Box<String> gets mocked as Box<Object>). It isn't possible to pass the necessary type information through GWT.create() (since it only takes a class literal), but there may be alternatives I'm missing.

Here's an example:

@RunWith(GwtMockitoTestRunner.class)
public class MyWidgetTest {

  interface Box<T> {
    T get();
  }

  static class MyWidget extends Composite {
    interface MyUiBinder extends UiBinder<Widget, MyWidget> {}
    private final MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

    @UiField
    Box<String> message;

    public MyWidget() {
      initWidget(uiBinder.createAndBindUi(this));
    }
  }

  MyWidget myWidget;

  @Before
  public void setUp() {
    myWidget = new MyWidget();
  }

  @Test
  public void simpleTest() {
    String message = myWidget.message.get();
  }
}

The mock for myWidget.message will be a raw Box, so get() returns a String, causing a ClassCastException:

java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$ef767c84 cannot be cast to java.lang.String
    at mockitobug.MyWidgetTest.simpleTest(MyWidgetTest.java:44)
        ...

I encountered this because if you write:

when(myWidget.message.get()).thenReturn("Hello");

Then the eclipse compiler will generate a string cast on the result of myWidget.message.get(), which causes the test to crash. (javac is unaffected, except for specific versions of javac9.)

Here's the complete repro:

mkdir -p src/test/java/mockitobug
curl https://gist.githubusercontent.com/cushon/50a9cbe451e7d6607a65/raw/8ccb8750c462b06d55109cfa2a6fcf7ff54f310c/MyWidgetTest.java > src/test/java/mockitobug/MyWidgetTest.java
curl https://gist.githubusercontent.com/cushon/50a9cbe451e7d6607a65/raw/94d77af02627d2b7b1756ff14e7a396514d7aeaa/pom.xml > pom.xml
mvn test
Running mockitobug.MyWidgetTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.565 sec <<< FAILURE!
simpleTest(mockitobug.MyWidgetTest)  Time elapsed: 0.237 sec  <<< ERROR!
java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$ef7df407 cannot be cast to java.lang.String
    at mockitobug.MyWidgetTest.simpleTest(MyWidgetTest.java:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
...
@ekuefler
Copy link
Collaborator

Yeah, the fact that we call GWT.create to populate UiFields will be problematic. We probably don't need to do this though - we could instead provide some sort of back door to invoke the underlying create code directly with additional type information. Would take some experimenting to figure out if we have the necessary information available and how to get it into Mockito.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants