Thursday, January 28, 2010

Problems with Unit Testing using GWTTestCase and GWT FlexTable()

I'm currently writing a web-application using Google Web Toolkit (GWT). To provide test-coverage for the app, I'm writing client-side unit tests that extend GWTTestCase.

Recently I found that some (previously passing) tests were failing. They threw a JavaScript Exception (com.google.gwt.core.client.JavaScriptException (null) null):

java.lang.RuntimeException: Remote test failed at 192.168.1.1 / Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1
at com.google.gwt.junit.JUnitShell.processTestResult(JUnitShell.java:1083)
at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1203)
at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1198)
at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1198)
at com.google.gwt.junit.JUnitShell.runTestImpl(JUnitShell.java:1104)
at com.google.gwt.junit.JUnitShell.runTest(JUnitShell.java:523)
at com.google.gwt.junit.client.GWTTestCase.runTest(GWTTestCase.java:406)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at com.google.gwt.junit.client.GWTTestCase.run(GWTTestCase.java:282)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:76)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: com.google.gwt.core.client.JavaScriptException: (null): null
at
...



The tests were failing on a GWT constructor call designed to create a new FlexTable:

FlexTable flexTable = new FlexTable();

Confused about what could be going on, I created a simple test which just called this constructor and found that it failed:

package test.client;

import com.google.gwt.junit.client.GWTTestCase;

import com.google.gwt.user.client.ui.FlexTable;

public class FlexTableTest extends GWTTestCase {

public void testFlexTable() {

FlexTable flexTable = new FlexTable();

}

@Override

public String getModuleName() {

return "test.FlexTableTester";

}

}


So - I created a completely new Project package from scratch and then found it passed just fine.

So - what was going on? Well it turns it was to do with my "myProject.gwt.xml" file. It seems that when you run the GWT tests using GWTTestCase, the code is compiled and run on a virtual browser - that may be different from the actual browser you're using to run the application in for manual testing or acceptance tests. Obvious, in retrospect!

Now, in my project, I had decided that since I was only running the application (and Selenium acceptance tests) in Internet Explorer browsers, I could save GWT compile time by only compiling one permutation of the GWT options - that for Internet Explorer.

To do this, I had introduced the following line of code into my "myProject.gwt.xml" file:

<set-property name="user.agent" value="ie6"/>

This code tells GWT to only compile one permutation - for Internet Explorer. However, it seems that the "virtual browser" used by GWTTestCase is not InternetExplorer (or at least not fully compatible with ie6 mode).

Having this property set to a particular browser (IE6) led to the problems I was having with my tests.

All I had to do was remove the <set-poroperty name="user.agent" value="ie6"/> tag and the tests worked perfectly.

Sunday, January 24, 2010

Selenium Testing GWT - upgrading to GWT 2.0 and Selenium 2.0a1

Recently, I'd written some acceptance tests for a Google Web Toolkit (GWT) based Web Application using SeleniumRC v1.0.1.

I was using GWT 1.7.1 and the tests were all working nicely. I had a Tree in the WebApp and was using GWT TreeItem elements for the nodes in this tree. I was also using GWT drag and drop (http://code.google.com/p/gwt-dnd/) to help with selecting and dragging TreeItems.

Over the last week I decided to upgrade the version of GWT from 1.7.1 to 2.0. As a result a bunch of Selenium functionality started failing. This included:
  • Selenium.click() method ceased to properly select a GWT TreeItem. A manual click will make the TreeItem go blue (ie. look selected and have "gwt-TreeItem-selected" class attribute in the DOM), but the selenium test doesn't.
  • Selenium.type("//*[@contentEditable='true']", newText) method ceased to properly enter text into an editable IFrame element.
  • Multiple Selection (via Drag and Drop GWT) failed to show visibly what was selected.
After trying a bunch of things, I came to the conclusion that there is significant Selenium functionality that worked correctly when testing GWT 1.7.1 web-apps that no longer works with GWT 2.0.

A simple example was the following code (which I asked about on StackOverflow) which tests the online GWT web-page demo for TreeItems and should click() on a treeItem. However, it doesn't produce the same functionality as a manual mouse-click:
package test;

import org.junit.Before;
import org.junit.Test;

import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;

public class TestTreeClick {
static Selenium selenium = null;

@Before
public void setUp() throws Exception {
if (selenium == null) {
selenium
= new DefaultSelenium("localhost", 4444, "*iexplore",
"http://gwt.google.com/samples/Showcase/Showcase.html#CwTree");
selenium
.start();
}
}

@Test
public void testingClicking() {
selenium
.open("http://gwt.google.com/samples/Showcase/Showcase.html#CwTree");
selenium
.click("gwt-debug-cwTree-staticTree-root-child0-content");
}
}
To try and alleviate this problem, I upgraded my selenium .jar file to the latest WebDriver backed Selenium (selenium-2.0a.1.jar) found at http://code.google.com/p/selenium/downloads/list. However, simply upgrading the .jar file and leaving the code the same doesn't actually use the WebDriver-backed Selenium compoments. To do this, I needed to modify the start-up code to be:

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverBackedSelenium;
import org.openqa.selenium.ie.InternetExplorerDriver;

import com.thoughtworks.selenium.Selenium;

public class TestTreeClick {

public static void main(String[] args) {
WebDriver driver = new InternetExplorerDriver();
Selenium selenium = new WebDriverBackedSelenium(driver, "http://gwt.google.com/samples/Showcase/Showcase.html#CwTree");
selenium
.open("http://gwt.google.com/samples/Showcase/Showcase.html#CwTree");
selenium
.click("gwt-debug-cwTree-staticTree-root-child0-content");
}
}
I found that the new Web-Driver backed Selenium does fix a number of the problems I'd found (or provides alternative ways to achieve functionality) - but not easily in a single step. However, it was good to find that for the click() example above, the new code works correctly with GWT 2.0, where as the old code does not.

However, at the moment (Jan 2010) this doesn't provide a really simple solution to my testing problems. Currently Web-Driver backed Selenium (Selenium 2.0a.1) does NOT provide a full set of testing functionality as found in earlier versions of standard Selenium. Some selenium methods that work in earlier versions have slightly different commands to make them work in the new version. Of the limitations/issues I've had, the key one is that Web-Driver backed Selenium does not currently provide support for right-click (context menu), and Ctrl-Click functionality.

In addition there are a number of minor changes to make things work. For instance, typing into an editable iFrame didn't seem to work using our old code:
  • Selenium.type("//*[@contentEditable='true']", newText)
Instead, we now have to use:
  • selenium.typeKeys(elementId, newText);
In summary, if I were to stop using the Web-Driver backed Selenium, the available test functionality is more complete, but unfortunately key bits I wish to test just don't work with GWT 2.0 code. I don't want to go back to GWT 1.7, so for the time being I'm developing my application without the ability to test Right-Clicks and having to re-write bits of the test code to work properly.

It isn't a huge deal, as I should have almost all of my right-click functionality in other menus, anyway - and hopefully the main testing comes from Unit tests and Integration tests. Nevertheless, just wanted to record these experiences here in case they help others understand what's going on when they upgrade - and what the impacts might be on Selenium tests.