Some WebLogic Workshop components can only be accessed from inside the server container. The most notable example is controls, which can only be instantiated by the framework and given to other components including other controls, JWS, JPF, and JPD classes. Therefore, to effectively unit test these kinds of components, your tests need to be running inside the server process.
When running against externally testable components, you run a JUnit test as a client process which issues HTTP requests to the components. To run a JUnit test inside the server container, you use the Cactus framework, a free, open-source code base that is part of the Apache Jakarta project. The Cactus website contains detailed information about how the JUnit client connects to the server and causes tests to be run. A summary of the mechanics follows.
When writing a JUnit test that you want to run inside the server using Cactus, instead of extending TestCase, you will usually want to extend ServletTestCase. Alternatively, you can have your suite() method return an instance of ServletTestSuite. When you run the client-side JUnit process on your test, it will detect that it’s a server-side test, and issue HTTP requests to a URL you specify. On the server, Cactus receives the requests, creates an instance of your test class, and runs the tests. It then returns the results to the JUnit client process, which displays them as if they were run like a standard JUnit test.
In order to use Cactus, you will need to configure your webapp to route incoming requests to Cactus appropriately, which is done by editing the web.xml deployment descriptor as is shown in the following snippet:
<servlet> <servlet-name>ServletRedirector</servlet-name> <servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletRedirector</servlet-name> <url-pattern>/ServletRedirector</url-pattern> </servlet-mapping>
Furthermore, both the server and client will need to have the JUnit, Cactus,
and test classes on their runtime classpaths. Generally speaking, you will
want to add the Cactus and JUnit JARs to your application’s APP-INF/lib
directory. You can also use Ant tasks to package up an EAR that contains all
of the classes you need to run your tests. When starting the JUnit client
process, you need to specify what URL Cactus should use when connecting to
the server with –Dcactus.contextURL=SomeURL. For more information, look
at Cactus
Task on the Apache Jakarta website.
If your production code that calls EJBs runs inside the same virtual machine as the EJB instances, you can make your test environment more closely match your production environment by using Cactus. This is shown in the following example.
In this example the above EJB test for the external component test has been converted to run inside the server. You can find the EJB test demonstrating Cactus at {UnitTestingWeb}/ejb/CactusEJBTest.java. It’s placed inside a web project so that it’s deployed to the server, although a Java project that builds a JAR file into the APP-INF/lib directory would also be deployed to the server. A code snippet for this test is shown below:
public class CactusEJBTest extends ServletTestCase {
private SampleSessionHome lookupHome() throws NamingException { Context ctx = getInitialContext(); // Lookup the bean's home using JNDI Object home = ctx.lookup("ejb.SampleSessionRemoteHome"); return (SampleSessionHome) narrow(home, SampleSessionHome.class); }
private Object narrow(Object ref, Class c) { return PortableRemoteObject.narrow(ref, c); } private Context getInitialContext() throws NamingException { System.out.println( "getInitialContext()" ); // Set up the environment properties Hashtable h = new Hashtable(); h.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); h.put(Context.PROVIDER_URL, "t3://localhost:7001"); return new InitialContext(h); } public void testEJB() throws Exception { assertEquals( 5, lookupHome().create().doSomethingBoring() ); }
...
If you look at the source code for the test, you will notice that it’s
virtually identical to the non-Cactus version. It extends ServletTestCase
instead of TestCase and has a different class and package name. It also prints
out some debugging output inside its getInitialContext() method so
that you can see that it is indeed running inside the server VM, but is otherwise
unchanged. Make sure that you build and deploy the application before trying
to run the test to ensure that the test class is indeed deployed to the server.
Controls are perhaps the most difficult components to test in WebLogic Workshop 8.1 because the user code does not directly instantiate them. Instead, you must allow the framework to give an instance of the control to one of the types of components that support controls, like a JPF. The following example shows how to make this happen.
In this sample, I have included an abstract base class, AbstractUnitTestController, for a Java Page Flow that can receive Cactus requests. To test a control, you create a subclass with JUnit test methods. Since your test case extends AbstractUnitTestController, it is actually a page flow controller, so the server framework will give your class an instance of controls to use. Also, you can use the design tools you are already familiar with inside the IDE to write your tests, which simplifies test creation. Note that you can easily adapt this page flow to test any Java control that you develop for your WebLogic Platform application.
The control that we’re testing is a service control automatically generated from the web service in the earlier example. The example also demonstrates a different way to test web services that doesn’t require building the Java client proxy. Workshop will automatically keep the service control synchronized with the web service. A snippet code of this control, UnitTestingWeb}/jws/JWSToTestControl.jcx, is shown below:
public interface JWSToTestControl extends com.bea.control.ControlExtension,com.bea.control.ServiceControl { public int square (int i); public java.lang.String hello ();
static final long serialVersionUID = 1L; } ...
The code of the test {UnitTestingWeb}/jws/newpageflow1/ ServiceControlTest.jpf, is shown here:
public class ServiceControlTest extends AbstractUnitTestController { /** @common:control */ private jws.JWSToTestControl jwsToTestControl;
/** @jpf:action */ protected Forward begin() throws ServletException { return super.begin(); }
public static Test suite() { return new ServletTestSuite( ServiceControlTest.class ); } public void testMethod() { Assert.assertEquals( jwsToTestControl.square( 2 ), 4 ); } }
There are two elements in the code that are required to make the test work correctly with Cactus (the rest is standard JUnit test code). First, the test needs to have a protected begin() method that just delegates to the superclass. Because in WebLogic Workshop 8.1 subclasses cannot inherit annotations, each test class needs to define a jpf:action so that it can receive HTTP requests. The superclass, found at {UnitTestingWeb}/jwsTestPageFlow/ AbstractUnitTestController.jpf, will take care of processing the request itself and calling the relevant test methods.
Second, the test needs a suite() method that adds the class to a ServletTestSuite. Since the class needs to be a Controller subclass to get control instances from the framework, this tagging tells Cactus to run the tests on the server instead of inside the client JUnit VM.
To run the test, be sure that you have built and deployed the application to your WebLogic Server. Then, use the controlCactus.bat file to start the client process and cause Cactus to run the tests in the server. As with the EJB Cactus example, you may need to edit the paths in the file to point to the correct directories on your machine.
To write tests for your own controls, you will just need to add the AbstractUnitTestController.jpf
class to your project and create your own subclass. The normal design tools
that help you edit page flow documents will also assist in creating tests
that use controls.