google / gwtmockito

Better GWT unit testing
https://google.github.io/gwtmockito
Apache License 2.0
157 stars 51 forks source link

A widget that has an existing parent widget may not be added to the detach list #58

Open lynx-r opened 8 years ago

lynx-r commented 8 years ago

I try to test my view with GwtMockito and have this problem:

java.lang.AssertionError: A widget that has an existing parent widget may not be added to the detach list
    at com.google.gwt.user.client.ui.RootPanel.detachOnWindowClose(RootPanel.java:137)
    at com.google.gwt.user.client.ui.RootPanel.get(RootPanel.java:211)
    at com.google.gwt.user.client.ui.RootPanel.get(RootPanel.java:151)
    at com.ait.lienzo.client.core.shape.Node.<clinit>(Node.java:112)
    at sun.reflect.GeneratedSerializationConstructorAccessor17.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:40)
    at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:59)
    at org.mockito.internal.creation.jmock.ClassImposterizer.createProxy(ClassImposterizer.java:128)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:63)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:56)
    at org.mockito.internal.creation.CglibMockMaker.createMock(CglibMockMaker.java:23)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:51)
    at org.mockito.Mockito.mock(Mockito.java:1243)
    at org.mockito.Mockito.mock(Mockito.java:1216)
    at com.google.gwtmockito.GwtMockito$Bridge.create(GwtMockito.java:317)
    at com.google.gwt.core.shared.GWT.createImpl(GWT.java:83)
    at com.google.gwt.core.client.GWT.create(GWT.java:86)
    at online.draughts.rus.client.application.home.PlayComponentView.initEmptyDeskPanel(PlayComponentView.java:195)

I try to create LienzoPanel in PlayComponentView like this:

Layer initDeskRect = GWT.create(Layer.class);
Rectangle contour = GWT.create(Rectangle.class);
...
lienzoPanel = GWT.create(LienzoPanel.class);
lienzoPanel.setBackgroundLayer(initDeskRect);
...

I investigated that there is problem in Node.java:

static
{
    RootPanel.get().getElement().getStyle().setProperty("webkitTapHighlightColor", "rgba(0,0,0,0)");
}

Is this problem on side of Lienzo?

ekuefler commented 8 years ago

Interesting. I tried making my own class containing the same static initializer block and GWT.creating that class in a test and it was able to call get on the RootPanel without any problems. Can you try creating a test that does nothing but call GWT.create(LienzoPanel.class) and seeing if that fails?

Is it possible some other part of your code is modifying the RootPanel in some way? From a quick glance at the RootPanel source, it seems like this would only happen if the RootPanel ended up getting inserted into the DOM hierarchy somehow, which I wouldn't expect to be caused by GwtMockito.

Whatever the root cause is, it's possible you might be able to work around the problem by annotation your test with @WithClassesToStub(RootPanel.class) or @WithClassesToStub(Node.class). That would prevent it from going down the code path that causes the exception, but might have other unanticipated side effects.

lynx-r commented 8 years ago

I tried to annotate class with @WithClassesToStub(Node.class) no changes but if I annotate it with @WithClassesToStub(RootPanel.class) I get this exception:

com.google.gwtmockito.GwtMockitoTestRunner$FailedCastException: The test failed with a ClassCastException. This often indicates that you are using a library that invokes JavaScriptObject.cast() to convert an object from one type to another in a way that can't be mimicked in pure Java. There are a few ways to deal with this:
  1) Inject the class that calls cast() into the class being tested so that you can replace it with a mock.
  2) Annotate your test class with @WithClassesToStub and pass in the class that is causing problems.
  3) If the class is part of GWT and not a third-party library, try reporting it on the issue tracker at https://github.com/google/gwtmockito/issues and it might be possible to  insert a workaround.

    at com.google.gwtmockito.GwtMockitoTestRunner$1.testFailure(GwtMockitoTestRunner.java:320)
    at org.junit.runner.notification.RunNotifier$4.notifyListener(RunNotifier.java:139)
    at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:61)
    at org.junit.runner.notification.RunNotifier.fireTestFailures(RunNotifier.java:134)
    at org.junit.runner.notification.RunNotifier.fireTestFailure(RunNotifier.java:128)
    at org.junit.internal.runners.model.EachTestNotifier.addFailure(EachTestNotifier.java:23)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:275)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at com.google.gwtmockito.GwtMockitoTestRunner.run(GwtMockitoTestRunner.java:367)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.ClassCastException: com.google.gwt.core.client.JavaScriptObject$$EnhancerByMockitoWithCGLIB$$9c9ac146 cannot be cast to com.ait.tooling.nativetools.client.NObjectBaseJSO
    at com.ait.tooling.nativetools.client.NObjectBaseJSO.createNObjectBaseJSO(NObjectBaseJSO.java:34)
    at com.ait.tooling.nativetools.client.NObjectJSO.make(NObjectJSO.java:25)
    at com.ait.lienzo.client.core.shape.Attributes.<init>(Attributes.java:83)
    at com.ait.lienzo.client.core.shape.Node.<init>(Node.java:144)
    at com.ait.lienzo.client.core.shape.ContainerNode.<init>(ContainerNode.java:55)
    at com.ait.lienzo.client.core.shape.Layer.<init>(Layer.java)
    at online.draughts.rus.client.application.home.PlayComponentView.initEmptyDeskPanel(PlayComponentView.java:199)
    at online.draughts.rus.client.application.home.PlayComponentView.<init>(PlayComponentView.java:120)
    at online.draughts.rus.client.application.home.HomeViewTest.testHomeView(HomeViewTest.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    ... 18 more

My test:

    PlayComponentView.Binder playBinder = GWT.create(PlayComponentView.Binder.class);
    DraughtsMessages messages = GWT.create(DraughtsMessages.class);
    AppResources resources = GWT.create(AppResources.class);
    playComponentView = new PlayComponentView(playBinder, messages, resources);
    assertNotNull(playComponentView);
lynx-r commented 8 years ago

LienzoPanel can be created as GWT.create(LienzoPanel.class) and new LienzoPanel(width, height).

scorpfrog commented 8 years ago

I was having the same problem, but adding:

@WithClassesToStub(RootPanel.class)

Fixed it, thanks @ekuefler