GWT Unit Tests running slowly? Use a TestSuite.

If you've written GWT unit tests for your app, you've probably realised that they run quite slowly. Most of the time, the delay seems to be in creating the context in which that test will run rather than in the test itself. This delay can be quite painful in situations where a large number of test cases are being run - as part of a Continuous Integration build process for example.

We were able to significantly reduce the running time of all the GWT test cases in our build by simply placing them in a TestSuite. Unfortunately, TestSuite is not one of the classes that is extended by the GWT's implementation of JUnit, but we didn't notice any real problems while using it.

Of course, since TANSTAAFL is one of the laws which governs our universe there is a trade-off to be made. When running a TestSuite you have to allocate enough memory for all the tests in all the TestCases in that TestSuite. GWT is quite a memory hog and can easily require over a Gigabyte of memory allocated to it. However this shouldn't pose a big problem and the reduction in build feedback time is more than worth it.

Using TestSuite creates a new problem though. People routinely forget to add the TestCases they create to the TestSuite which is run by the build. Consequently you end up with bits of functionality being broken but not being detected simply because the relevant tests are not being run during a build.

Around the third time this happened, I got frustrated and decided to automate the process. We used Ruby to write a little script which would pick up all the TestCases in a particular directory and add it to a template. It's pretty rough but it works, so whatever...
Just remember that it doesn't auto-generate imports, so you'll have to add those to the template manually. It isn't hard to modify the script to do this too, but it happened sufficiently rarely in our case that I didn't bother.

On Windows, we needed to check in just two files to use Ruby - msvcrt-ruby18.dll and ruby.exe

Here's the code listing:

suite.template

import junit.framework.TestSuite;
import junit.framework.Test;
import com.whatever.imports.you.need.*;

public class GWTTestSuite extends TestSuite {
  public static Test suite() {
    // Five hashes are what the ruby code replaces
    Class[] tests = {
      #####
    };

    TestSuite suite = new TestSuite();
    for (int i = 0; i < tests.length; i++) {
      suite.addTestSuite(tests[i]);
    }
    return suite;
  }
}


gen.rb

java_files = Array.new
gwttestfiles = Array.new

jfiles = File.join("**", "*.java")
#Change working directory to the GWT test directory
Dir.chdir("\\your\\project\\path\\testgwt")
Dir.glob(jfiles) {|x|
  java_files<<x if "#{x}".include?("Test")
}

java_files.each do |element|
  file = File.new(element)
  matched_lines = file.select{|line|   line.match('\s*public\s+void\s+test.*')}
  if matched_lines.size > 0
    name = File.basename(file.path)
    gwttestfiles<<name.gsub("\.java","\.class")
    puts "Located: " + name
  end
end

classes_string = gwttestfiles.inject { |result, klass| result + ",\n" + klass }

file = File.new("\\your\\project\\tools\\ruby\\suite.template")
File.open("GWTTestSuite.java","w") do |output|
  file.each do |line|
    line.include?("#####") ? output.write(classes_string) : output.write(line)
  end
end


The Ant target to run the ruby script:

<target xmlns="" name="generate.gwt.test.suite">
  <exec dir="\your\project\tools\ruby\" executable="\your\project\tools\ruby\ruby.exe" failonerror="true">
    <arg value="gen.rb"/>
  </exec>
</target>
Post a Comment