JUnit and WCS6

Introduction

JUnit is a simple framework to write repeatable tests. As to why we need to write unit test try the link http://junit.sourceforge.net/doc/testinfected/testing.htm

Above link give us a heads up on why we need unit testing and how to work with JUnit.

This document is going to focus on how we can write unit test for WCS 6 classes with JUnit. After going through the above link and if you try to write a test case to check for example the WCS LogonCmd class then you will end up spending the whole development time in figuring out why we cannot run JUnit directly with WCS 6 classes.

Simply put WCS 6 classes’ needs to be run inside the WCS server environment. To achieve this JUnit alone will not suffice. We need two more components, namely

JUnitEE provides a Test Runner which outputs HTML and a servlet which can be used as an entry point to our test cases. Building our test harness as a standard J2EE web application means:

  • Our tests are packaged conveniently into a .war file which can easily be moved between servers; we can leave the .war file in the main .ear file and simply avoid enabling the test web application on the production server.
  • Our test classes will be dynamically reloaded by the app server (assuming the server supports this).
  • Our test cases look just like the production code, and can use the same beans we use as a facade for our EJBs.

Cactus is a simple test framework for unit testing server-side java code. Please note that Cactus is retired by Apache and since no other server-side testing framework is compatible with JDK 1.4, we will be using Cactus strictly for writing test cases where you may need HTTPServlet request and response. For other normal scenarios we use only JUnit

Note: Cactus has been discontinued by Apache

Versions to download

JUnit 3.8.1 - http://download.java.net/maven/2/junit/junit/3.8.1/

JUnitEE 1.11 - http://sourceforge.net/projects/junitee/files/junitee/1.11/

Cactus 1.5 - http://archive.apache.org/dist/jakarta/cactus/binaries/

Setting up Test Web Module

Create a Web module in your project workspace and add all the downloaded JARs to the project class path. Deploy the WAR file in your Enterprise Application namely WC and provide its own context so that we can invoke the Test WAR file using that context. Below are the lines from WC Application deployment descriptor file

<module id="WebModule_1266272689905">
      <web>
          <web-uri>IntegrationUnitTestWAR.war</web-uri>
          <context-root>/webapp/wcs/unittest</context-root>
      </web>
</module>

Next step is to create a servlet for JUnitEE Test Runner, which will scan the web module for test classes ending with ‘Test’ and will display it to the user for further processing. Since this servlet will be part of our WCS EAR, all WCS resources loaded will be available to the test runner. So when we invoke a test class from the runner, we can successfully execute a test case

Below or the line for the JUnitEE Test Runner servlet from the web.xml of the newly created test web module IntegrationUnitTestWAR

<servlet>
    <servlet-name>JUnitServlet</servlet-name>
    <description>JUnitEE test runner</description>
    <servlet-class>org.junitee.servlet.JUnitEEServlet</servlet-class>
</servlet>
   
<servlet-mapping>
    <servlet-name>JUnitServlet</servlet-name>
    <url-pattern>/JUnit/*</url-pattern>
</servlet-mapping>

From the above configurations we can invoke the Test runner using the URL

https://localhost/webapp/wcs/unittest/JUnit

Please note that WCS server should be running to invoke the above URL

To set the Cactus for testing classes with need for http request/response we need to setup 2 more servlet in the same test web module. Below are the lines for the same from the web.xml file

<servlet>
    <servlet-name>ServletRedirector</servlet-name>
    <display-name>ServletTestRedirector</display-name>
    <servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
    <init-param>
            <param-name>param1</param-name>
            <param-value>value1 used for testing</param-value>
      </init-param>       
</servlet>
<servlet>
    <servlet-name>ServletTestRunner</servlet-name>
    <display-name>ServletTestRunner</display-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>
<servlet-mapping>
    <servlet-name>ServletRedirector</servlet-name>
    <url-pattern>/ServletRedirector</url-pattern>
</servlet-mapping>

After the above configuration we are good to write our test cases.

Writing JUnit/Cactus test cases

For example purpose we will test the LogonCmd of WCS. Create a class under the Java Resources folder of your test web module with the Cactus class ServletTestCase as the base class.

public class LogonJunitCmdTest extends ServletTestCase {
}

For any WCS controller command the main resource is CommandContext. So we need to create this CommandContext with default values and use this when calling the LogonCmd

To set the CommandContext use the setup() method of JUnit as below. Also set the request properties with logon id and password to use the LogonCmd

protected void setUp() throws Exception {
    constants = new TestConstants();
    cmdContext = new ViewCommandContextImpl(null);
    StoreAccessBean strBean = new StoreAccessBean();
    strBean.setInitKey_storeEntityId(constants.getStoreId());
    strBean.commitCopyHelper();
    cmdContext.setActivityId(-1);
    cmdContext.setStoreId(new Integer(constants.getStoreId()));
    cmdContext.setLanguageId(new Integer(constants.getLangId()));
    cmdContext.setActiveOrganizationId(new Long(constants.getActiveOrganizationId()));
    cmdContext.setWebpath(constants.getWebPath());   
    cmdContext.setEncoding(constants.getEncoding());
    cmdContext.setStore(strBean);
    cmdContext.setCommandName(LogonCmd.class.getName());
    cmdContext.setCountry("US");
    cmdContext.setCurrency("USD");
    cmdContext.setDeviceType(new Integer(-1));
    cmdContext.setParentOrg(constants.getActiveOrganizationId());
    cmdContext.setUserId(new Long(-1002));
      
    // The below request and response objects are implicitly provided by Cactus
    cmdContext.setRequest(request);
    cmdContext.setResponse(response);
   
    cmdCont = cmdContext;       
   
    // Below are the required values for LogonCmd   
    req = new TypedProperty();
    req.put("logonId",constants.getUserName());
    req.put("logonPassword",constants.getPassWord());
    req.put("URL","/");
    req.put("storeId",constants.getStoreId());
    req.put("langId",constants.getLangId());
    req.put("catalogId",constants.getCatalogId());
    req.put("reLogonURL","/");
       
    super.setUp();
       
    }

We can run the actual test by using the below function

public void testPerformExecute() throws Exception{
                     
    LogonCmd logonCmd = (LogonCmd) CommandFactory.createCommand(LogonCmd.class.getName(),
            new Integer(constants.getStoreId()));
    logonCmd.setCommandContext(cmdCont);
    logonCmd.setRequestProperties(req);
    logonCmd.setAccCheck(false);           
    logonCmd.execute();           
    assertTrue(logonCmd.getUserId().toString(),true);
}

After completing the Test class we can invoke it using the JUnitEE Test Runner as below

After selecting the test case and clicking Run, we get the below page if the test is success else we will get corresponding error message

Automating the test process using ANT

We can automate the testing process using JUnitEE ANT tasks

For this purpose JUnitEE contains two Ant tasks:

  • the JUnitEEWar task creates the .war file we need to run our tests inside a servlet container (similar to the War task included in the standard Ant distribution)
  • the JUnitEE task allows us to start execution of our server-side tests from within our ant build script (similar to the JUnit task included in the standard Ant distribution)

Below are the 2 tasks

<taskdef name="junitee" classname="org.junitee.anttask.JUnitEETask">
  <classpath>
    <pathelement location="lib/junitee-anttask.jar"/>
  </classpath>
</taskdef>
<taskdef name="juniteewar" classname="org.junitee.anttask.JUnitEEWarTask">
  <classpath>
    <pathelement location="lib/junitee-anttask.jar"/>
  </classpath>
</taskdef>

Now that Ant knows about the JUnitEE tasks we will first use the JUnitEEWar task to create the test.war. Consider that we have a project with the following characteristics:

  • the classes to test are located in build/classes
  • the test classes are compiled in the directory build/test
  • the test classes names have Test as a prefix
  • the .jar files you need are stored in the lib directory

The following Ant task

<juniteewar destFile="myTest.war">
  <lib dir="lib" includes="junitee.jar"/>
  <lib dir="lib" includes="junit.jar"/>
  <classes dir="build/classes">
    <include name="**/*.class"/>
  </classes>
  <classes dir="build/test">
    <include name="**/*.class"/>
  </classes>
  <testcases dir="build/test">
    <include name="**/Test*.class"/>
  </testcases>
</juniteewar>

will build a file called myTest.war which contains

  • junit.jar and junitee.jar under WEB-INF/lib
  • the classes to test and all test cases under WEB-INF/classes
  • a file named WEB-INF/testCase.txt which contains a list of all test case names
  • a file named index.html which allows you to run tests manually using your browser
  • the deployment descriptor WEB-INF/web.xml which makes myTest.war a deployable web archive

Deploy the test.war and run the tests

At this point we have a deployable .war file, so in the next step we have to tell Ant how to deploy the war and how to run the tests. Let's start with the second step and define a target named do-run-tests:

<target name="do-run-tests">
  <junitee url="http://your.server:port/myTest/TestServlet" printsummary="true">
    <test runall="true"/>
  </junitee>
</target>

Executing this target will run all test cases included in myTest.war and print a summary of the test results to the console window.

Now we have a deployable war file and an Ant target to run the tests. The last missing step is to automate the deploy-run-undeploy cycle, and this is the moment when Jakarta Cactus enters the stage. Cactus provides an Ant task that does the following:

  • deploy your application and wait until deployment is finished by the application server
  • run your tests
  • and finally undeploy your application

This is exactly what we need to reach our goal. Therefore we define another external task:

<taskdef name="runservertests"
  classname="org.apache.cactus.ant.RunServerTestsTask"
  classpath="lib/cactus-ant.jar"/>
Now we are able to define our run-tests target:
<target name="run-tests">
  <runservertests
    testURL="http://your.server:port/myTest/TestServlet"
    startTarget="deploy-test-ear"
    stopTarget="undeploy-test-ear"
    testTarget="do-run-tests" />
</target>