google / gwtmockito

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

gwtmockito + gwtquery #31

Open spg opened 10 years ago

spg commented 10 years ago

One hurdle that I can't seem to get over when trying to unit test views is I often use gwtquery. Unfortunately, using gwtquery makes for untestable views.

Do you think a collaboration between you and @jDramaix would be possible in order to make gwtquery play nice with gwtmockito? gwtquery seems like a very popular library in the GWT world, so it would make sense to allow it within a unit test context.

Thanks a lot for your great work :)

ekuefler commented 10 years ago

This seems like it might be hard - GwtQuery is naturally very DOM-centric, so it's not really going to be possible to emulate much of the interesting behavior in Java which basically rewriting an entire browser. It wouldn't be too hard to stub out methods so that it doesn't run into exceptions when you execute $() from within a test, but I'm not sure this would be useful.

What would you want a test leveraging GwtMockito and GwtQuery to look like?

nloke commented 9 years ago

@ekuefler I agree with you that GwtQuery is naturally very DOM-centric and plus they are static methods call in general where we cannot even verify. Unless we can make GwtMockito to work together with PowerMockito where we can delegate to use GwtMockitoTestRunner, which currently is not possible as both PowerMockito and GwtMockito's classloader modifcation(not sure if i got the terms correct) interferes with each other.

However that being said, the problem that is causing GwtMockito tests to fail when there is a Gquery code is due to the infamous JavaScriptObject#cast(). I suppose for now if you can help point out some workaround so that we can stub out all the GQuery objects to be a mock, then we will be okay, just like how you get around the GWT.create issue.

ekuefler commented 9 years ago

Which class contains the call to JSO.cast()? A stack trace might be helpful. It's possible you can work around the problem in your test by annotating the test with @WithClassesToStub, passing the class that's making the cast() call. That will cause all methods in that class to be stubbed out so that it never has to do the cast. Of course, this could cause strange behavior in any class that depends on that class working in a particular way.

nloke commented 9 years ago

In GQuery.java there is cast happening there. If we tried to stub GQuery, we will fail eventually due to the following limitation on mockito for "final":

public static final Class<Effects> Effects = GQuery.registerPlugin(
    Effects.class, new Plugin<Effects>() {
        public Effects init(GQuery gq) {
            return new Effects(gq);
        }
    });
ekuefler commented 9 years ago

Looks like the code you're referencing is from https://github.com/gwtquery/gwtquery/blob/master/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Effects.java#L103, which is indeed doing a lot with statics, finals, and javascript casts that are going to interfere with mocking.

Have you tried annotating the test with @WithClassesToStub(GQuery.class)? I think that'll be enough to shut down the code path leading to the snippet you pasted. It's a sledgehammer though that will make pretty much all of GQuery not work, so you'll probably be pretty limited in how much your tests can do. But it might be enough if you're testing some business logic in a class that only uses GQuery incidentally.

timeu commented 8 years ago

@ekuefler: Ran recently into this issue and tried to add GQuery.class to @WithClassesToStub but ran into following issue:

java.lang.ExceptionInInitializerError
    at com.google.gwt.query.client.GQuery.<clinit>(GQuery.java:153)
    at com.gmi.nordborglab.browser.client.mvp.ApplicationView.<init>(ApplicationView.java:145)
    at com.gmi.nordborglab.browser.client.mvp.ApplicationViewTest.setUp(ApplicationViewTest.java:32)
...
Caused by: org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class java.lang.Class
Mockito cannot mock/spy because :
 - final or anonymous class
    at com.google.gwtmockito.impl.StubGenerator.invoke(StubGenerator.java:116)
    at com.google.gwt.query.client.GQuery.registerPlugin(GQuery.java)
    at com.google.gwt.query.client.plugins.QueuePlugin.<clinit>(QueuePlugin.java:36)
    ... 31 more

I also tried to add QueuePlugin to @WithClassesToStub but it does not help :-/