Expresso Unit Testing Framework.

Expresso builds upon the JUnit testing framework to provide an automated unit testing environment.

Version:

Expresso 5.x

Author:

Michael Rimov, Ivan Ivanov

Introduction

JUnit's documentation can be found here. It was necessary to extend JUnit because Expresso applications expect certain system configurations to be available at any given time. These include database configuration, as well, as web-application location (which would normally be gleaned off of the servlet container). Further extensions were needed to be able to run test cases within a servlet container.

To accomplish this, we branched into two areas:


Client Side Testing

To provide a framework that we can perform client side testing with all standard expresso services such as logging, database connection management, and many others we provide the following class:

com.jcorporate.expresso.services.test.ExpressoTestCase

To create your own test case use the following sample as a model:

import junit.framework.*;
import junit.extensions.*;
import com.jcorporate.expresso.services.test.*;


public class SampleTestSuite extends ExpressoTestCase {
	public SampleTestSuite(String name) throws Exception {
	  super(name);
	}


	public static void main(String[] args) throws java.lang.Exception {
	  //Set the system properties we need
	  junit.textui.TestRunner.run (suite());
	}


	public static junit.framework.Test suite() {
return new TestSuite(SampleTestSuite.class);
} public void test1() { //Do your testing code here }
public void test2() { //Do more testing here } public void test3() { //You can do more testing here } /*....Do as many test cases as you want, all with the public void signature */ }

How to run this test case:

To run the test case, Expresso requires that you set the following Java Virtual Machine parameters to

junit.argv.configDir=<Full Path To Your Configuration Directory>
junit.argv.webAppDir=<Full Path To Tomcat's Webapp Directory>

For example, on the command line you will have the following:

-Djunit.argv.configDir=/usr/local/tomcat4/webapps/yourApp/WEB-INF/config -Djunit.argv.webAppDir=/usr/local/tomcat4/webapps/yourApp -Djunit.argv.logDir=/usr/local/tomcat4/webapps/yourApp/WEB-INF/logs

If you extend your test cases from com.jcorporate.expresso.services.test.ControllerTestCase or com.jcorporate.expresso.core.utility.DBToolTests you need add another database context in your expresso-config.xml file called 'test' that should point to your test database. Creating another database for testing your expresso application is recommended since expresso reinitializes it every time the tests are ran an you might lose the data in your development database. Some authors really suggest that you have your test database different from the development one to prevent damaged data among other reasons. While this is a matter of taste and habits, what the Expresso test system requires is that you have set up another context named test and what database it refers to is up to you.

The JUnit framework will automatically use Java Introspection to run all the public(void) test cases. See the JUnit documentation on how to use the framework to signal if a test passed or failed. Also, see the class: com.jcorporate.expresso.core.misc.CookieTests to see a fully functional sample of integration with JUnit Framework.

All expresso test case classes will play well with the JUnit test runners. So if you wish to run a text-based only test case, you can run junit.textui.TextRunner. If you wish to run within a nice graphical environment, you can run your test suite with:

To run all test cases within a graphical environment, run the class:

junit.swingui.TestRunner

use the UI to pick the class you wish to test and let her run.

The -c will determine which test suite to run. You can use your own test cases, or use the class sample above to run all Expresso test cases at once.

Picture of JUnit Swing Test Runner
Picture of JUnit's Swing Test Runner doing its stuff.

Writing your test case guidelines

There are a couple of necessary guidelines to follow when writing your own test cases.


Schema Testing

When we're first creating our application, we want to make sure it runs correctly, right? But as we're developing our schema, and creating out databases, we have to each time go through the monotonous task of dropping a database, recreating it, starting up Expresso, running DBCreate, etc. This quickly becomes quite a chore. So we've created a special test case for testing Schema Creation. Here's all the code you need. The following example is borrowed directly from eForum and is complete:

import junit.framework.*;
Import com.jcorporate.expresso.services.test.*;
Import com.jcorporate.expresso.core.utility.DBToolTests; public class SchemaTests extends DBToolTests { public SchemaTests(String testName) throws Exception { super(testName); }
 public static void main(String[] args) throws Exception {
	   //Set the system properties we need
	   junit.textui.TestRunner.run (suite());
   }
   public static junit.framework.Test suite() throws Exception {
   		return DBToolTests.suite();
   }
   protected void setUp() throws Exception {
	   //System must be initialized prior to instanatiating the schema instance
	   TestSystemInitializer.setUp();
	   Class c = Class.forName("com.jcorporate.eforum.ForumSchema");
	   schemaList.add(c.newInstance());
	   super.setUp();
   }
}

That's it! To make this code work for your schema all you do is change the ONE line in setup() that loads ForumSchema to use your own schema class. When this class is run, it will make sure that all tables are removed from the Test database, and then attempt to create all schemas (Including Expresso's) in the test database. It will also run separately populateDefaultValues() and setupDefaultSecurity() so you can make sure that everything is cooprating.

This kind of testing is an extremely helpful and powerful tool for testing your schemas as you're building them.


Server Side Testing

The bulk of your web application is going to be logic within an Expresso Controller object, or possibly a standard Servlet. Unfortunately, standard JUnit client-side testing procedures definitely fall flat in being able to cope in this area. Enter Apache's Cactus project.

Cactus was designed to provide the unification of JUnit's client side testing API and in-container server-side testing. What it does is initiate a test case on the client side so you can stuff all the HTTP request parameters with everything you need such as state to request, login cookies, etc. The test case then serializes itself to the server, where a specially designed servlet loads your test class and executes the code on the server side. The result is then sent back to the client side so you can examine cookies sent back to client or other results.

Here is an animation created by the Apache Cactus documentation project that highlights what was said above.

The rest of the cactus documentation set can be found here.

If you are writing a servlet, the Cactus documentation will be sufficient to get you started on a servlet. Even if you're writing a controller test case, it is strongly recommended that you browse through the Cactus website to get an idea of how a Cactus test case is written since the controller test cases are extensions to a standard Cactus test.

If you are writing a controller test harness, there are quite a few things to consider such as having a running underlying database, negotiating security, and parsing the controller response. To assist in this, we've provided the classes:

com.jcorporporate.expresso.services.test.ControllerTestCase
and
com.jcorporate.expresso.services.test.ControllerTestSuite

 

A Quick Sample

Below is a sample showing a simple usage of a controller test case (suite). All comments with a number are footnoted/explained in the area below the code sample.

Import com.jcorporate.expresso.services.test.*;
Import com.jcorporate.expresso.core.controller.*;
Import org.apache.commons.cactus.*;
Import org.apache.commons.cactus.util.*;
Import junit.framework.*;
Import org.w3c.dom.*;
Import java.net.HttpURLConnection;
import java.io.IOException;
import javax.servlet.*;
Import java.util.*;
 

Public class SampleControllerTest extends ControllerTestCase {
	public SampleControllerTest(String name) {
	  super(name, "com.jcorporate.expresso.services.DBSecurityMatrix"); //1
	}


	public static void main(String[] args) throws Exception {
	   junit.textui.TestRunner.run(suite());  //2
   	}
 	public static TestSuite suite() throws Exception {
	   ControllerTestSuite cts = new ControllerTestSuite();  //3
	   cts.addReadOnlySchemaDependency("com.jcorporate.expresso.core.ExpressoSchema"); //4
	   cts.addTestSuite(SampleControllerTest.class);
	   return CTS;
   }
 
  /* Executed on the client side of the web request */ 
  public void beginPromptState(WebRequest theRequest) throws Exception
{
super.logIn(theRequest); //5
super.setupParameters("prompt",theRequest); //6
}
  /* Executed on the server side of the web request */ 
   public void testPromptState() throws Exception {
	   ControllerResponse response = super.controllerProcess();  //7
	   assertTrue("Got a null response", response != null);


		assertTrue("Title returned from the controller state.",
				response.getTitle().length() > 0);             //8
    }


	/*....Do as many test cases as you want, all with the public void signature */
}

Code Explanations:
  1. The second parameter to the ControllerTestCase's constructor is the name of the class you wish to test. In this case it's Expresso's own DBSecurityMatrix controller.
  2. By writing a main method that calls JUnit's Text Test Runner, you have the ability to test out your classes and test cases by just running this class.
  3. The controller test suite is responible for having a proper underlying test database running. If one doesn't exist, it will create it for you automatically, (including schemas), and if you need it to be erased when you're done, it will do that too.
  4. This line(s) tells the test suite what schemas you must have up and running for your test case to work properly. To add more schemas, simply repeat the call for other schemas. Use addReadOnlySchemaDependency() if you DO NOT do any modifications to the underlying databases by running your controller. This will tell the system to not delete your created test database at the end of a run since it's still in pristine state. Call addSchemaDependency() if your controller modifies the underlying database in any way.
  5. super.logIn() set's the appropriate cookies for a user Admin with a blank password. This is what is created by default in a test database. If for some reason you do not want to login with this username and password, you can use the method super.logIn(request, "username", "password");
  6. The first parameter of setupParameters() is the name of the state that you will be testing with this test run. In this case it's "prompt". The function also sets the appropriate parameters for the Controller to return an XML formatted output.
  7. The first confusing thing to know is that testXXXXXX() RUNS ON THE SERVER. Everything up to this point has been running on the client side. To process the controller, however, your job is simple, just call the ControllerTest case's controllerProcess() function and the testing framework will call your controller state automatically. This function will return a ControllerResponse object back to you, in effect, the data that the controller generated.
  8. Once you get the ControllerResponse object you can walk through it's Inputs,Outputs,Blocks and Transitions to make sure they are as you would expect them to be. Use JUnit's assertTrue() and fail() functions to test if things were properly sent back.
  9. If you want to run your tests from your browser (this is a feature provided by Cactus), add the following to your web.xml file at the appropriate places:

    <servlet>
        <servlet-name>ServletTestRunner<servlet-name>
        <servlet-class>org.apache.cactus.server.runner.ServletTestRunner<servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletTestRunner</servlet-name>
        <url-pattern>/ServletTestRunner</url-pattern>
    </servlet-mapping>

    Then start your servlet container and type in the address bar like the following:

       http://localhost:8080/yourApp/ServletTestRunner?suite=com.your.package.YourTestCase

    where yourApp is the web context of your application (could be blank) and com.your.package.YourTestCase is the fully qualified class name of your test case.
    The results of the tests (fails, successes, and stack traces) will appear in your browser in XML format or you can format it according to Cactus' documentation.

    Note the 'suite' is a parameter expected by Cactus' ServletTestRunner and that your test cases should be visible to your servlet container.

  10. If you want a more automated way of running your tests than calling them from a browser, you should consider Ant and Ant tasks, implemented in Cactus.

You can take a look at the class com.jcorporate.expresso.services.controller.test.DBSecurityMatrixTests for a complete working controller test case that tests a couple of states including getting the results of a form POST.

Running The Test Case

Software Requirements

To run the Cactus tests you need the following software pieces.

Configuring Cactus

There is one step that has to be done for Cactus to run properly on your system.

Running the test cases

You can run any JUnit test runner for a controller test case including SwingTestRunner for graphical test suites.

Test Case Conclusions

It should be noted that to follow Extreme Programming's (XP) unit testing strategies, you will want to chain your test cases together. Check the example of com.jcorporate.expresso.core.ExpressoTestSuite for a clear example of this usage.


Copyright © 2002-2004 Jcorporate Ltd. All rights reserved. Copyright Privacy

Last Modified: 26-Aug-2003