-
Notifications
You must be signed in to change notification settings - Fork 51
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
"java.lang.ClassCastException: com.google.gwt.core.client.JavaScriptObject$$EnhancerByMockitoWithCGLIB$$29edae7f cannot be cast to com.google.gwt.user.client.Element" while trying to test a widget extending SimpleLayoutPanel #4
Comments
Hm, it definitely shouldn't matter what your widget is extending. I'll dive into this tomorrow to try and figure out why it's breaking. Thanks for the detailed report! |
So the underlying problem here is pretty interesting (skip to the bottom if you just want to see the fix). The issue is with the methods from DOM.java in GWT that create elements. LayoutPanel uses these directly instead of going through the nicer Document API. They look like this: public static Element createDiv() {
return Document.get().createDivElement().cast();
} The issue is the cast() method, which is designed to coerce any JavaScriptObject into any other. This is fine when GWT is compiled to Javascript, but it causes some havoc in Java when you try to cast one class to something it doesn't extend. That's what's happening in GWT here - It would be nice if we could just stub calls to cast() to return new mocks of the appropriate type (so it's not really a cast at all), but unfortunately it's impossible to figure out what type we actually want to cast to at runtime since that information is only contained in the generic definition of cast, which is lost at runtime. Too bad cast doesn't take the target class as an argument. So implementing cast() in a completely satisfying way probably isn't going to happen. What I can do is stub out the create methods in DOM entirely to return mock Elements. This should avoid breakages when using code that calls into DOM as LayoutPanel does. The only case that this won't cover is when code does a weird cast like So, long story short, I've pushed a workaround for this in commit ce7323e. Want to try building GwtMockito from HEAD and seeing if that solves your problem? |
Thank you for the very detailed answer. Unfortunately, the problem has been solved only partially. First of all, I can successfully build commit ce7323e but the latest one (8f7f4af) results in three errors during Maven test phase. You can find the Surefire output here: https://gist.github.com/konradstrack/3a457db40d38a154b976#file-gistfile1-txt But when I use ce7323e, the test passes if I don't attach any child widgets to MyPanel. So, this is alright: public class MyPanel extends SimpleLayoutPanel {
public MyPanel() {
Label label = GWT.create(Label.class);
label.setText("It's my panel");
// add(label);
}
} But the original test results in an AssertionError. So, if I try to add a Label: public class MyPanel extends SimpleLayoutPanel {
public MyPanel() {
Label label = GWT.create(Label.class);
label.setText("It's my panel");
add(label);
}
} I get:
|
Oops, last-minute visibility change broke the tests. Pushed a fix for that. The next problem is interesting. The reason GwtMockito works well with Composites is that all the code for widgets lives behind fields that can be mocked out. This gets harder when you're extending a real widget. For your case, it would work fine if you extended Composite and wrapped a LayoutPanel since we could mock out the add() call, but since you're extending LayoutPanel instead it gets harder to mock since your class inherits a real add method. But extending non-Composite widgets is common enough that GwtMockito should find a way to make it work. I think what it can do is stub out the implementations for all of the methods in a bunch of common base widget classes - UIObject, Widget, Composite, and all the Panels seem like good candidates. This effectively allows us to "mock" the superclass of the widget. It's still probably not as testable as using a Composite since you can't stub and verify the methods individually, but it should at least allow subclasses to be instantiated and their methods be called without breaking. I've implemented this in 11cb40a. Let me know if that helps with your use case. |
I have just tested GwtMockito from HEAD on the original test case and it seems to work perfectly fine. Thank you for fixing the problem, and in such short time. There is however one other possible use case that I don't really know how to handle, and if it is in any way supported by GwtMockito right now. Everything seems to be fine if you extend something that comes from GWT. But if, for instance, my widget would be extending something like a SimpleContainer from GXT it would fall into similar problems. Would it be enough to create an additional provider for SimpleContainer, or is there another mechanism that would allow to provide mocks for parents which are not standard GWT widgets? PS. Sorry for prematurely closing the issue. |
It probably doesn't make sense to add SimpleContainer to the list of things GwtMockito knows about, since that would introduce a dependency from GwtMockito to GXT. However, it probably would be a good idea to make the list user-configurable. I'm not aware of an easy way to pass arguments to test runners, but we could probably accomplish this by making GwtMockitoTestRunner overridable, such that the set of classes to stub could be configured via a protected method. So if you're using GXT, you could define an override of GwtMockitoTestRunner that adds all the GXT base classes to the stub list, and use that in your tests, How does that sound? |
I totally agree that it doesn't make sense to add any weird dependencies to GwtMockito - GXT is not the only set of widgets out there, and SimpleContainer is probably not the only thing that may need mocking. The proposed solution seems very good, and pretty convenient too - for sure much more convenient than stubbing classes in every test case. |
Okay, I've provided a protected method that you can override in 2e9f9f4. Check it out and let me know if it works like you want it to. |
Hi! I've took over that issue from @konradstrack. Finally it works! The only thing is that the default class list is not full at all. For example the ListBox expose this method. private SelectElement getSelectElement() {
return getElement().cast();
} And it's failing for the mentioned reason. |
Yeah, the default class list is intentionally pretty narrow and I'm not including every GWT widget in it. I want to start by being fairly conservative about what goes on the list such that there's as little magic happening as possible. I think I'll wait a while to see what people think of the current setup and then re-examine if there's demand for expanding the default list. |
This worked well for me. For Sencha GXT, I am using the following:
Also, Sencha GXT actually calls
|
I'm having this identical issue, and upgrading to GWTMockito 1.1.1 did not help In my case I'm using a custom DataTable that extends com.google.gwt.user.cellview.client.CellTable What fails is line 613 in CellTable table = getElement().cast(); java.lang.ClassCastException: com.google.gwt.user.client.Element$$EnhancerByMockitoWithCGLIB$$55b005a0 cannot be cast to com.google.gwt.dom.client.TableElement The problem is getElement return type is How would I go about fixing this with explicit mocking in my test? I tried the following, but it doesn't compile due to the sibling issue: when(myTable.getElement()).thenReturn(mock(TableElement.class)); But honestly I'm not even sure how this could work, because since this happens in the constructor of CellTable, there is a catch 22. I can't mock the object until I construct it and I can't construct it without getElement be mocked. How would one mock out a base class? |
There is a further issue with the suggested workaround in #4 (comment) As suggested, the following snippet fixes the problem for Labels created as part of code executed within the test: DivElement divElement = mock(DivElement.class);
when(divElement.getTagName()).thenReturn("div");
final Document document = Document.get();
when(document.createDivElement()).thenReturn(divElement); However, if the Label is part of a class or base class for a type that the test is attempting to @GwtMock, the failing assertion code will execute before the mocks can get setup and executed |
@nikp - if your problem is a misbehaving base class in @Override
protected Collection<Class<?>> getClassesToStub() {
Collection<Class<?>> classes = super.getClassesToStub();
classes.add(CellTable.class);
return classes;
} Can you let me know if that helps? If so I could add CellTable to the list of classes to stub by default. |
Thanks @ekuefler, I will try that! |
Hi. There is a problem with com.google.gwt.user.client.ui.Image class too. Image uses private inner class com.google.gwt.user.client.ui.Image.UnclippedState and I don't have access to it. When I create new Image("anyUrl") (in my view class which I want to test) I have an exception: com.google.gwt.user.client.Element$$EnhancerByMockitoWithCGLIB$$5830e7d1 cannot be cast to com.google.gwt.dom.client.ImageElement from UnclippedState's method: @OverRide Because Image is mocked it returns Element which cannot be cast to ImageElement. Do you have any ideas how to fix it? |
@avetokhin I'm pretty sure that should just be a matter of stubbing out
|
I am was getting the ClassCastException for CellTable as mentioned 7 months ago. Using GwtMockito 1.1.3 from maven repo Is there a solution to this? |
Spent a long time trying to find a good workaround for Image (which does a lot of weird things casting Elements back and forth in a way that makes Java unhappy), without much luck. For now I've added it to the list of classes to stub by default, which should fix the issues you describe (but may cause others, let me know if so). I've also fixed a few issues related to CellTable. You should be able to see those and the Image fixes if you run against the latest snapshot - is anyone still seeing ClassCastExceptions? |
Hi, i am getting a classcast exception when writing the Junit testcases for the following scenario. |
Is there a known workaround for the |
@timeu Actually i have created a mehtod to mock the JavaScriptObject.createArray() and return the javascriptobject. and i mocked it .and after that i mocked the javascriptobjectobejct.cast and returned the jsarray. and this resolved my issue. |
@ekuefler - I still get class cast exceptions in my code when I run GWT mockito test cases. Below is the snippet code which causing the cast issue. Caused by: java.lang.ClassCastException: com.google.gwt.dom.client.Element$$EnhancerByMockitoWithCGLIB$$4ba5b663 cannot be cast to com.google.gwt.user.client.Element. Any workaround for this issue? |
@vikram-github Yes even i faced the same issue,then i created two public methods in my class and Now after this ,in your test file use spy Mocikto.spy() and mock the above two methods. |
@GwtMock Image mockImage;
Mockito.when(mockImage.getElement()).thenReturn(ImageElement.class); @justinmk do you have a working example? |
Hey, I am experiencing the following class cast exception issue: Caused by: java.lang.ClassCastException: com.google.gwt.dom.client.Text$$EnhancerByMockitoWithCGLIB$$4050ddf1 cannot be cast to com.google.gwt.dom.client.Element |
@khouari1 Using |
Let's take the following example:
And a test case:
Running this test case will result in the following exception:
If MyPanel extends Composite, everything works fine, but with SimpleLayoutPanel (or just SimplePanel) the result is the same as the one above.
Is this a known limitation?
In many cases widgets can be refactored to extend Composite, but is there a way of testing with gwtmockito those that have to extend something else than Composite, or is using GWTTestCase required in such cases?
The text was updated successfully, but these errors were encountered: