Testing inside a servlet with Ant, TestNG, and Groovy

In a previous post, I talked about how I run my TestNG unit/integration tests from within an EJB. The EJB implemented the old 2.0 standard, which meant that maintaining all of the configuration metadata was a continual effort sink. I recently moved it to simply use a servlet, which I should have done from the beginning. Since I could no longer use the EJB client code, I had to also write an HTTP client to invoke the servlet and process the results. This post describes the Java code invoking the TestNG tests from within a servlet, the Groovy HTTP client that invokes this servlet, and the Groovy Ant task configuration and code to invoke the client script.

First, the test servlet class was created, which extends HttpServlet and implements it with the following method (this is nearly the same as the previously posted code in the EJB):

    public void doGet(HttpServletRequest request, HttpServletResponse response)
                                        throws ServletException, IOException {

        // Sets the content type of the response
        ServletOutputStream out = response.getOutputStream();

        try {

            TestNG tng = new TestNG();

            tng.setTestClasses( new Class[] {
            } );

            final StringBuilder sb = new StringBuilder();

                new TestListenerAdapter() {
                    @Override public void onTestFailure(ITestResult tr) { 
                        sb.append("F{" + tr.getTestClass().getName() + "." + 
                                          tr.getMethod().getMethodName() +"}"); }
                    @Override public void onTestSkipped(ITestResult tr) { 
                        sb.append("S{" + tr.getTestClass().getName() + "." +
                                          tr.getMethod().getMethodName() +"}"); }
                    @Override public void onTestSuccess(ITestResult tr) { 
                        sb.append("."); }



        } catch (Exception e){
            StringBuilder ex = new StringBuilder();
            for (StackTraceElement ste : e.getStackTrace()){
                ex.append(ste.toString() + "\n");
            out.println("Aack! " + e.toString() +"  " +  ex);
        } finally {

I find the Groovy Ant task to be an excellent way of enhancing the power of Ant. Because of it's XML structure, there are many things that are difficult or impossible to do in an Ant task. Even a simple if/then is awkward. This make complex Ant code not only difficult to write, but more importantly difficult to read. Groovy allows to easily break out of the Ant jail and write like a Real Programmer. The concision of the Groovy language, closures, and built-in data structure syntax allows you to express in a couple of lines withing the Ant file something that would a page of Ant code or several lines of Java. Most importantly when working with many other developers, Groovy is enough like Java that most programmers can infer the meaning of most Groovy code even if they have no experience with the language.

I couldn't find any instructions with "cut and paste this text into your Ant build file to use the Groovy task", so here's what I use:

<property name="ant.home.dir" value="" />
<property name="junit.jar" value="" />
<property name="tools.dir" value="" />

<property name="groovy.dir" value="${tools.dir}/groovy" />
<property name="groovy.lib.dir" value="${groovy.dir}/lib" />
<path id="groovy.lib">
    <pathelement location="${ant.home.dir}/lib/ant-1.7.0.jar" />
    <pathelement location="${ant.home.dir}/lib/ant-junit-1.7.0.jar"/>
    <pathelement location="${ant.home.dir}/lib/ant-launcher.jar"/>
    <pathelement location="${junit.jar}"/>
    <pathelement location="${groovy.lib.dir}/antlr-2.7.6.jar"/>
    <pathelement location="${groovy.lib.dir}/asm-2.2.jar"/>
    <pathelement location="${groovy.lib.dir}/asm-analysis-2.2.jar"/>
    <pathelement location="${groovy.lib.dir}/asm-tree-2.2.jar"/>
    <pathelement location="${groovy.lib.dir}/asm-util-2.2.jar"/>
    <pathelement location="${groovy.lib.dir}/bsf-2.4.0.jar"/>
    <pathelement location="${groovy.lib.dir}/commons-cli-1.0.jar"/>
    <pathelement location="${groovy.lib.dir}/commons-logging-1.1.jar"/>
    <pathelement location="${groovy.lib.dir}/groovy-1.5.4.jar"/>
    <pathelement location="${groovy.lib.dir}/jline-0.9.93.jar"/>
    <pathelement location="${groovy.lib.dir}/jsp-api-2.0.jar"/>
    <pathelement location="${groovy.lib.dir}/mockobjects-core-0.09.jar"/>
    <pathelement location="${groovy.lib.dir}/mx4j-3.0.2.jar"/>
    <pathelement location="${groovy.lib.dir}/servlet-api-2.4.jar"/>
    <pathelement location="${groovy.lib.dir}/xpp3_min-"/>
    <pathelement location="${groovy.lib.dir}/xstream-1.2.2.jar"/>

<property name="groovy.lib" refid="groovy.lib" />

<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"

I think you can exclude the junit jar if you're not using it, but there's a version included with the Groovy distro if not.

The Groovy Ant task allows you to either call a separate Groovy file or embed Groovy code directly between the <groovy> tags. Below we'll call a separate Groovy script, and in a future post I'll do some embedded Groovy (specifically, querying the server to set the http.port property dynamically).

  <property name="http.port" value="" />
  <property name="app.name" value="" />

  <target name="run-tests" >

      <groovy src="${test.scripts.dir}/servletclient.groovy" >
          <arg value="http://localhost:${http.port}/${app.name}/"/>

      <echo message="result running test: ${test.result}"/>

          <equals arg1="${test.result}" arg2="true" />
              <touch file="${test.results.dir}/t.foo.servlet.dif" />                   
              <delete file="${test.results.dir}/t.foo.servlet.suc" />               
              <touch file="${test.results.dir}/t.foo.servlet.suc" />                   
              <delete file="${test.results.dir}/t.foo.servlet.dif" />               

The <arg> tag values are accessed in the groovy script via the array "args", with the indexing beginning at 0. Values are returned to the Ant context by setting values in the map named 'properties'. All properties in the Ant context are passed into the Groovy script in this variable, and are automatically unmarshalled back to Ant with any new entries (since Ant properties are immutable, you can change the values of the existing keys, but they won't be retained).

The Groovy HTTP client I use to access the servlet is below. This prints the content retrieved from servlet to standard out (so it appears in the ant log) and sets the value of the ant property test.result via the 'properties' map. If the output contains at least one '.' (meaning a test ran) and no instances of 'F' or 'S' (indicating a failed or skipped test), the result is 'true'. I'm certain this code could be shorter, but it's based on the longer DEWD client from Tony Landis, so I only stripped it down as far as needed to work for my purposes.

// Run tests within a J2EE container by calling a servlet.
// print the retrieved output to standard out

uri = new URI(args[0])
socket = new Socket(uri.getHost(), uri.getPort())

contentLen = 0
writedata = "GET " + uri.getPath() + " HTTP/1.0\r\n" +
"Host: " + uri.getHost() + "\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n" +
"Content-Length: " + contentLen + "\r\n\r\n" +
"Connection: close\r\n\r\n"  
writer = new PrintWriter(socket.getOutputStream(), true)

// read and throwaway header
reader = new DataInputStream(socket.getInputStream())
c = null
while (null != ((c = reader.readLine()))) if(c=='') break

// read content
def row
content = ''
while (null != ((row = reader.readLine()))) content += row + "\n"  
// Response from the servlet should consist  of a string of periods
// (each period representing a successful test).
properties["test.result"] = (! (content =~ /[FS]/)) && (content =~ /\./)

println content


Ant, TestNG, and Groovy — such a powerful trio!

One thought on “Testing inside a servlet with Ant, TestNG, and Groovy

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">