PDA

View Full Version : Mocks, testing and GXT philosophy



eugenparaschiv
9 Nov 2009, 10:13 AM
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();
This means that the architecture allows for mocking; it basically means that it doesn't make any decisions that could block the ability to mock, like using global state or using GWT.create() and then using the return value (I'll go into that later).
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";
Of course this is not the only place where the GWT class uses global state, and so it's not the only place where mocking is blocked.
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.

jpnet
9 Nov 2009, 11:08 AM
I couldn't agree with you more. Thanks for posting this.

It would be nice to get a response from someone on the GXT team.

-JP

Andrey Minogin
9 Nov 2009, 1:25 PM
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:
@SuppressStaticInitializationFor("com.extjs.gxt.ui.client.GXT")
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.

eugenparaschiv
17 Nov 2009, 6:16 AM
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.
Eugen.

sven
17 Nov 2009, 6:17 AM
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.

eugenparaschiv
17 Nov 2009, 6:28 AM
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.

WolfDogg
17 Nov 2009, 8:56 AM
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!

Ozzy
19 Nov 2009, 8:32 AM
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.

-- Patrick.

eugenparaschiv
19 Nov 2009, 8:34 AM
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.
Thanks again.
Eugen.

eugenparaschiv
21 Nov 2009, 11:40 AM
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
As simple as that.
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.
Cheers.
Eugen.

eugenparaschiv
25 Nov 2009, 2:46 PM
Any thoughts on this possibility from the GXT team? Any other ideas are welcome of course.

sven
25 Nov 2009, 2:51 PM
As i already said we will look into this.

Have you tested your interface approach? It does not make much sense this works and directly it is not working. As to test correctly, the same things have to be created/initiated.

You should also thing if it actually make sense to the a UI the way you test it. There are much better ways to test a ui build with html eelements, and sure, these are slower, but it should be clear why.

sven
25 Nov 2009, 2:53 PM
This means that the architecture allows for mocking; it basically means that it doesn't make any decisions that could block the ability to mock, like using global state or using GWT.create() and then using the return value (I'll go into that later).

There are even a coulpe of GWT widgets that use GWT.create, and it is correctly to do so. Started by the most used one, FocusWidget.

Also same GXT widget use this. But GXT also uses the GXT class to gather informations, not only GWT.create. There is no way around using the GXT class.

cravemusic
14 Dec 2009, 5:59 PM
I think the point is that with the proper use of interfaces, you're not actually instantiating the GWT version of the class, with all of its dependencies, including GWT.create(). You should be able to mock these objects via their provided interface, because you need an instance of an object with that interface to accomplish a specific test. The subtle point is that the test is not dependent on any of the implementation of that interface.

mattpainter
19 Jan 2010, 4:27 PM
I'm not sure why people's GWTTestCase unit tests are taking so long per test. Using Eclipse I am choosing to run all GWT unit tests in a package for a single (my only) module. With this, there is an initial delay of 20-30 seconds as the code is compiled and the server starts, but after this there is no additional delay per test, no recompilation and no tearing down of servers between tests.

I have implemented the Adaptor pattern for GXT widgets as independently suggested earlier in this thread, but as this isn't the most elegant approach I may just bite the bullet (if my experience isn't just as a result of the skeletal nature of my tests).

I guess it depends on one's testing techniques. Mine are occasionally test-first, but often a more pragmatic (IMHO) malleable technique of development of a chunk of code, followed by a mop-up period of testing and documentation.

I don't care if that remark is going to cause TDD zealots to get hot under the collar ;)

M

eugenparaschiv
8 Feb 2010, 1:22 AM
Ok, in answer to your first point - it does add the compile time of the project to the actual time running the test; you're lucky and you only have one single module, but this isn't always the case. And even without this compile time, the tests themselves still run orders of magnitude slower than in a real VM (at least 100 times slower). Now add that to the compile time and you end up with a few minutes running the whole suite, which makes it pretty much impossible to do TDD (not saying you should do TDD, only that it's much harder to do it if you want to).

There are others disadvantages to working with GWTTestCase, like the fact that it doesn't encourage you do decouple your logic from your UI code among other things. Another disadvantage is that you cannot use any mocking framework (Mockitor, EasyMock) inside GWT code (obviously), and I personally find the ability to mock out some visual component invaluable.

Related to Adapter, I agree it adds to much complexity to justify the advantages. As I said before, GWT solves this elegantly by using interfaces and I'm sure that GXT will follow at some point. Before that happens, what I did (this is also the suggestion of the Spiral blog post on the main ExtJS page) is simply to extend each GXT visual class with my own, and implement interfaces for what I need. I then use the interfaces in the code instead of using the GXT classes directly (IContentPanel instead of ContentPanel). That has worked well in testing, as I can now mock almost everything. Not only that, but I think the code is cleaner as well, and the cost of maintaining these interfaces is minimal, because in fact they don't define anything new.
As for the testing approach, this mix of TDD and non-TDD works well, as long as the focus is to increase the TDD time and focus and decrease the non-TDD focus.
Cheers.

snobbles
12 Feb 2010, 7:51 AM
Any updates on whether this will be addressed in GXT3?

mgreen
15 Feb 2010, 10:37 AM
This is a critical point for us too. I would love to see this on the road map for GXT 3.

In the meantime, for GXT 2, I suggest we set up a project on Google Code for the Adapter classes everyone is forced to write on their own.

Cheers,
Mattias

eugenparaschiv
10 Mar 2010, 5:11 AM
Ok, as a result of this discussion and some other threads all over the GXT forums, I created a google code project to address the problem. It is a thin layer of interfaces and simple implementations that sits on top of the GXT framework. The main purpose is to provide a simple way of creating code that is completely testable and mockable via mocking frameworks (such as Mockito).
http://code.google.com/p/gxt-interfaces/
I will try to upload the artifact on some public maven repos as well, but in the meantime, just import the code. Hope it helps. Eugen.

eugenparaschiv
19 Apr 2010, 10:58 AM
OK, to answer a previous question about whether the interface approach is tested or not, the gxt-interfaces project I have created (http://code.google.com/p/gxt-interfaces/) is being used in our application and is successfully solving the problem with not being able to mock out the visual components. Any thoughts on the possibility of having this kind of functionality in a future release of GXT? Hope the project helps, I am actively maintaining it and the documentations is coming along as well. Any thoughts or suggestions? Thanks. Eugen.

sven
19 Apr 2010, 11:01 AM
As i already said, GXT3 will contain the required changes. http://www.extjs.com/products/gwt/roadmap.php

WolfDogg
20 Apr 2010, 6:58 AM
The GWT event handlers and interfaces on the GXT roadmap are great news!

jorel2
3 May 2010, 12:08 PM
I've run into the same problem trying to mock out portions of my application for testing. ...
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 :).
-- Patrick.

Ozzy,

Would you be interested in posting your adapters for others to use?

regards,
Jorel

corm
4 Jul 2010, 5:17 AM
We finally managed to go some way towards keeping Test driven development with JMock and GXT 2.1 by making a base class for our test classes which would use javaassist to disarm GXT. Extract as follows : (still had to use an interface approach for a few components, particulary GXT combobox could not make a mock one with the static design, etc., as mentioned in thread, but this got us passed alot)

------------------------------
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.BeforeClass;

import com.google.gwt.junit.GWTMockUtilities;

public abstract class BaseTestUI {

private static boolean isDisarmed;

protected Mockery context = new JUnit4Mockery();

{
context.setImposteriser(ClassImposteriser.INSTANCE);
}

@BeforeClass
public static void setup() {

if (!isDisarmed) {
GXTMockUtilities.disarm(); // our own class
GWTMockUtilities.disarm();
isDisarmed = true;
}

}

}
------------------------------

Where GXTMockUtilities is as follows:


import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;

public class GXTMockUtilities {
private static boolean alreadyDisarmed;


public static void disarm() {

if (!alreadyDisarmed) {
try {

final CtClass gwtClazz = ClassPool.getDefault().get("com.google.gwt.core.client.impl.Impl");

final CtClass gxtClazz = ClassPool.getDefault().get("com.extjs.gxt.ui.client.GXT");

final CtClass returnClazz = ClassPool.getDefault().get("java.lang.String");

renameAndReplaceMethod(gwtClazz, returnClazz, "getModuleBaseURL");
// renameAndReplaceMethod(gwtClazz, returnClazz,
// "getHostPageBaseURL");
renameAndReplaceMethod(gxtClazz, CtClass.voidType, "init");
// Load the classes in the classloader
gwtClazz.toClass();
gxtClazz.toClass();
alreadyDisarmed = true;

} catch (final NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final CannotCompileException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

// this is somewhat dangerous as you will be reloading classes
// the JVM doesn't really like this
public static void restore() {

}

private static void renameAndReplaceMethod(final CtClass clazz, final CtClass returnClazz, final String methodName)
throws NotFoundException, CannotCompileException {

final CtMethod oldMethod = clazz.getDeclaredMethod(methodName);

// rename old method to synthetic name
final String dummyMethodName = methodName + "$impl";
oldMethod.setName(dummyMethodName);

// create a new dummy method
// can't simply copy as the methods are native
final StringBuilder body = new StringBuilder();
body.append("{");
body.append("return \"\";");
body.append("}");

final CtMethod newMethod = CtNewMethod.make(Modifier.STATIC | Modifier.PUBLIC, returnClazz, methodName,
new CtClass[] {}, new CtClass[] {}, body.toString(), clazz);

clazz.addMethod(newMethod);

}

}