9 Nov 2009 10:13 AM #1
Mocks, testing and GXT philosophy
I have been struggling with some of the inner workings of the GXT framework for a few days now, and I just wonder if anyone has any solution to these problems, or if this is on the radar for a future release. So this post is about the testability options in GXT.
The functionality I am after is simple: mocking GXT visual elements and then using them in tests that run in the JVM (not extending GWTTestCase).
The reason for this is obvious - tests build up in a large application and it's no longer viable to run 2000 tests, multiple times during the day, when the runtime is 30 minutes. This is the whole point of having the tests, right? The solution is also simple (or so it seems) - run the tests in a JVM, many orders of magnitude faster (by many I mean 1000 times faster or more).
GWT seems to enable this easily. I have not looked at the code, seeing how I don't really use GWT widgets, but I've done a simple test with a Button, and it is mocked just fine (I'm using Mockito, but any mocking framework will do):
GWTMockUtilities.disarm(); final Button mockButton = Mockito.mock( Button.class ); // GWT button GWTMockUtilities.restore();
So it is possible.
Now the same test, only with the GXT button fails, and fails in such a way that I cannot seem to find any simple solution around the problem. The fault is with the GXT class itself: it uses global state, so each time it's loaded (and it's loaded every time you need to create some visual element, like a button), it does this:
public static String SSL_SECURE_URL = GWT.getModuleBaseURL() + "blank.html";
There are many ways to tackle this problem, some very un-disruptive to the API.
So keeping a constructive and positive attitude to all of this - I just want to start a discussion and possibly get a feeling about the future development of GXT.
It goes without saying (but I'm going to say it anyway), the benefits of allowing this are huge and not to be underestimated. This is one of the few real drawbacks of using GXT over GWT, and it just feels that it would really benefit the platform if the architecture would allow for real testing (of which mocking is a crucial part of).
Any thoughts on this would be cool.
9 Nov 2009 11:08 AM #2
9 Nov 2009 1:25 PM #3
Thanks for your post!
I should add my conclusions after a week of struggle with GXT mocking.
There's a nice solution for mocking in non-friendly environments - Powermock. It's an extension for EasyMock and partially Mockito. It allows suppressing static initializers, "bad" superconstructors and methods. This seemed to be a solution, but unfortunately there came a lot of problems.
1.) It appeared to be very very slow. When you write something like:
it takes about 30 seconds for a single test to run. And there is such delay on every new unit test run.
Powermock team was surprised too, and another problem is that I can't provide them with a working example because the last GXT version is not free. Nevertheless they now think about this problem and I hope they find some solution soon. This seems to be a problem with Javassist byte-code manipulations.
2.) Another problem is that there is too much unfriendly code. I suppressed: GXT.clinit() and Component.clinit() but there appeared problems with GXT.Messages initialization and so on. There're too many native static and constructor initializers on all the levels of widgets hierarchy. It would be hard to suppress them all anyway.
At the moment I see that the only solultion is to wrap GXT with some interfaces suitable for mocking, but you can see that this is a great and ungrateful work.
GXT Team! Please, pay attention to this problem. It is very important for us to write verified code and fast unit tests.
17 Nov 2009 6:16 AM #4
Of course a possible solution for this would be Adapter pattern - create interfaces for each GXT visual element (such as Button) and only use the interfaces in the code. Then mocking evidently becomes easy, but at the cost of increased complexity and lost readability, especially if you're not familiar with the pattern.
Any official response to this? I would very much appreciate it, even if it's "No", so that at least I know if there's any point waiting for this or I should just start thinking about Adapter.
Thanks for the responses to this thread.
17 Nov 2009 6:17 AM #5
We can look into this for GXT3. But there is no way we can do changes in GXT2 because this changes would be braking once.
17 Nov 2009 6:28 AM #6
OK, cool, thanks for the quick response. So if this could be an GXT 3.0 goal, then an updated roadmap would also help. Cheers.
17 Nov 2009 8:56 AM #7
We are encountering these problems building a large enterprise wide-app, so the possibility of GXT3 changing to suit the GWT interfaces would be great news!
19 Nov 2009 8:32 AM #8
I've run into the same problem trying to mock out portions of my application for testing.
GWT has solved some of these problems recently by providing interfaces in appropriate places so that widgets are unnecessary to test "most" application code. For example look at the GWT classes HasClickHandlers and ClickHandler and ClickEvent.
I've been using the MVP pattern as described by Ray Ryan of Google. In this pattern, all the widgets are in the View and the Presenter, which does the bulk of the application work, integrates with the widgets via interfaces, like HasClickHandlers. So the view doesn't have a Button : getSaveButton() method, instead it has HasClickHandlers : getSaveButton(), the presenter then attaches listeners to the HasClickHandlers interface for save and can respond to click events.
What I did was to add adapters around GXT widgets so that I could turn a ButtonEvent into a ClickEvent. The GXT button adapter implements HasClickHandlers, and a user of the apapter simply registers a ClickHandler and process the ClickEvent(s) generated. I've adapted other GXT widget classes and added my own events in some places (as opposed to using GWT events).
This model works well, expect for the fact that I have to write all the adapters .
I think the GWT folks have the right idea and I would like to see this model implemented in GXT so that it is easy to separate the view from the rest of the application and therefore allow the bulk of an applications code to be easily tested via mocks.
19 Nov 2009 8:34 AM #9
Thanks for your response, it was very informative. I've been flowing about the same path, MVP and have begun experimenting with the Adapter pattern. The only problem with that pattern is that it introduces another level of indirection in the system, which means code that is harder to understand for someone new, even for somewhat not really familiar with the pattern. This is a real problem for a team, and not to be glossed over.
The use of this pattern would also mean keeping the adapter in sync with the widget on each new GXT release, and for each widget.
Yet another downside would be choosing the API for the adapter, because you may want to interact with the passive View in different ways from the Presenter, not only by registering a listener on it. And so if the View publishes the adapter, instead of the real widget, then the adapter has to pretty much have the whole widget API, or nearly; and that is a large enough API to make you think twice about this.
So taking all of this into consideration, I am still reluctant to make this step large scale, although the benefits would be tangible.
The choice is made easy when thinking about GXT 3.0, which may be a long ways away, but should and hopefully will really deal with this issue, as it is clearly a problem now, and many people would benefit from it.
21 Nov 2009 11:40 AM #10
Ok, I was saying in the beginning of this discussion that there may be ways of handling this without any breaking changes to the API. This idea has been trickled in the discussion above, and I'm going to reiterate it here clearly; it's simple and straightforward actually. The point of this is obvious: if a simple enough solution is presented, perhaps it can be integrated into a 2.x branch instead of the 3.x one. So here goes.
Each GXT widget has a public API (obviously), and so an interface can be put on top of that class without ANY change to the API; the interface can have the standard name, such as: IButton, ILayoutContainer, etc, and should include the public API of that class. It should also extend the interface of the parent class:
LayoutContainer implement ILayoutContainer ContentPanel extends LayoutContainer implements IContentPanel IContentPanel extends ILayoutContainer
Evidently, this is a very simple solution that would completely solve the testing and mocking problem without having to purge the actual system of all that global state code, which may be an extensive task and may indeed mean breaking API changes and who knows what else (of course that cleanup could still be done for a future release).
First, what would the disadvantages of including these interfaces:
The development time needed - the task of integrating interfaces like this should be mechanic and straightforward, and shouldn't take to long in an IDE (extract Interface makes this a non-issue).
The interfaces would be unfocused, mixing in responsibilities - this would be a problem if an interface would mean the end of the line, but fortunately, it doesn't. So the API can evolve to include interfaces that are actually related to clear responsibilities in the future (whether that means using GWT interfaces or not) without any problems. The new interfaces can simply be inherited by the widgets as needed, without causing problems. The point of these interfaces is not to define responsibilities, but just to publish the API of the class.
The advantages are clear as well: no breaking API changes whatsoever, the possibility of complete mocking and testing in a JVM. The old tests that used to extend GWTTestCase can be migrated simply. Now, the question of the client code - any class that used to reference a widget directly can now choose to do so through the interface, with minimal (and mostly mechanic) changes.
I stress "choose", because you can simply remain oblivious that these interfaces even exist, or you can start integrating them into the code gradually. So a test suite that used to take the better part of an hour could now be made to run in under a minute (and thus many many more times a day).
Ok, that's about it, waiting for responses to this not-revolutionary solution, disadvantages I might have missed, and if possible the thoughts of the GXT team.