2011/08/05 - Jakarta Cactus has been retired.

For more information, please explore the Attic.

"Can't find resource for cactus" error message

If you get the following stack trace:

		java.lang.ExceptionInInitializerError: java.util.MissingResourceException:
		Can't find bundle for base name cactus, locale en_GB
		at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:707)
		at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:679)
		at java.util.ResourceBundle.getBundle(ResourceBundle.java:546)
	

it simply means that you have not put the cactus.properties file in your CLASSPATH (i.e. the directory which contains this file should be in the CLASSPATH). This file is the Cactus configuration file and is required. Check the Cactus Configuration Howto page for more information.

[top]

How can I have a web.xml that is valid both for testing and for production?

Cactus needs to have a few entries set in the web.xml file (redirector definition and mappings mostly). The application to test also needs its own entries. What would be nice would be to be able to use the same web.xml file in the build process for both application testing and deployment to production.

Here's how I accomplished conditional inclusion of Cactus web.xml configuration. My web.xml file is capable of working standalone without requiring any filtered copy to work. In Ant I'm doing this:

		
		<copy todir="${admin.build.dir}/WEB-INF" file="web/admin/WEB-INF/web.xml" overwrite="yes">
		<filterset>
		<filter token="start.cactus.config" value="--&gt;"></filter>
		<filter token="end.cactus.config" value="&lt;!--"></filter>
		</filterset>
		</copy>
	

In web.xml I have this:

<![CDATA[
	<!-- Cactus configuration
	Note: Do not place any XML comments in this Cactus configuration section
	(Ant's filtered copy is used to activate this configuration when the test
	web application is built)
	-->
	<!-- Begin Cactus Configuration @start.cactus.config@
	<servlet>
	<servlet-name>ServletRedirector</servlet-name>
	<servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
	</servlet>
	
	<servlet>
	<servlet-name>JspRedirector</servlet-name>
	<jsp-file>/somewhere/jspRedirector.jsp</jsp-file>
	</servlet>
	
	<servlet-mapping>
	<servlet-name>ServletRedirector</servlet-name>
	<url-pattern>/ServletRedirector/</url-pattern>
	</servlet-mapping>
	
	<servlet-mapping>
	<servlet-name>JspRedirector</servlet-name>
	<url-pattern>/JspRedirector/</url-pattern>
	</servlet-mapping>
	
	@end.cactus.config@ End Cactus Configuration -->
	]]>
[top]

I'm getting a java.io.StreamCorruptedException: InputStream does not contain a serialized object error. What does it mean?
This error only appears for Cactus 1.3dev (before 21/04/2002) and below. Cactus 1.3 no longer uses a serialized object to return test result. It uses an XML string.

This exception results when client-side Cactus code attempts to talk to a misconfigured installation of Cactus on the server side. The reason for the obscure exception is this: Cactus makes two requests to the server. In the first, the client-side test runner requests that such-and-such a test be run on the server (an oversimplification, please see: XXXX). Immediately after the response from the server the client code makes a second request, this time for the results of the server-side test. Cactus is supposed to send back a serialized Object containing the results. Cactus then attempts to deserialize this Object from the input stream in the response. If Cactus is installed properly on the server, this process works transparently. If not (for instance the second request does not even arrive at a Cactus redirector) then the response to the second request (which very well may be a 404 or 500 error) will not contain the object that client-cactus expects. This leads to the StreamCorruptedException.

When troubleshooting this error, the most likely prospect is that the requests from client-Cactus are not even reaching server-Cactus. This can happen when the cactus.properties contains a bad entry. For instance, I have run into this problem when building several different testing-applications. I tried to run my tests against http://localhost/webapp1/ServletRedirector/ when I needed to be running against http://localhost/webapp2/ServletRedirector/. This can also happen if your web.xml file contains an error, or does not properly map the URLs contained in cactus.properties to the Cactus redirectors.

A good way to check whether your requests are reaching a Cactus redirector is to manually enter in the URLs for all of the redirectors you use into the navigation bar of your web-browser. Actually the Cactus redirectors provide a URL just for that: http://localhost/webapp/ServletRedirector?Cactus_Service=RUN_TEST (this also works for the other redirectors).

If you call http://localhost/webapp/ServletRedirector directly, you'll get a 500 error and get a stack trace (in the log or along with your error page) that says . This is because the Cactus redirector expects HTTP parameter from the Cactus client side.

Another likely error is that your server-side classpath does not contain a copy of the cactus.jar. (Cactus is effectively not present on the server.) According to e-mails on the Cactus user list, the StreamCorruptedException could also result when you are using the wrong version of the servlet.jar in some area (any version prior to 2.2, or using servlet.jar 2.2 with a Cactus-1.3 installation).

[top]

How can I test the chaining of several HTTP requests?

Let's imagine that I have one servlet that performs an HTTP redirect to a JSP page (or to another servlet). How do I test it with Cactus?

Short answer: you don't !

Long answer: Cactus is meant to be a unit testing tool, meaning it lets you perform tests for a given method and then it lets you assert the result from that method.

In the above scenario, a typical Cactus test will consist in verifying that the return HTTP response is an HTTP redirect (by checking the HTTP response code - 302 - in the endXXX() method) and possibly also checking the URL of the redirect page.

Then, as a second test, you could (if you wish) verify that calling the URL of the JSP does return such HTML content (using HttpUnit integration for example). Note that this second test is rather a functional test than a unit test but it can still be viewed as unit testing the doGet() method of the JSP ...

[top]

The runservertests Ant task hangs when my server is started

When I use the runservertests Cactus custom Ant task, it starts my server allright but then hangs and the tests are not executed. What is happening?

It means that the testURL attribute you've specified in runservertests is not a valid URL. It must be a URL that is valid when the server is up. To diagnosis the problem simply open a browser once your server is started (i.e. when the task hangs) and type the URL in your browser. An alternative to debug is also to run Ant in debug mode (ant -debug [your target]).

With Cactus 1.3, the correct URL to call is the following, which is always valid: http://localhost:8080/webapp/ServletRedirector?Cactus_Service=RUN_TEST. Of course replace webapp by your webapp context and replace the port by the one you're using.

[top]

I'm getting a "not a valid response error message. What does it mean?

When I run my Cactus test, I'm getting the following:

		testRequestDispatcherInclude(org.apache.cactus.sample.TestSampleServlet)
		org.apache.cactus.util.ChainedRuntimeException:
		
		Not a valid response
		at
		org.apache.cactus.client.WebTestResultParser.readRootElement(
		WebTestResultParser.java;org/apache/cactus/util/log/LogAspect.java(1k):134)
	

It means that Cactus could not connect to the server side properly. There could several reasons for this: a valid Redirector URL but not pointing to the Redirector, a secured resource, etc.

To easiest way to diagnosis the problem is to enable Cactus logging (see the Configuration Howto for how to do this). The cactus client log (cactus_client.log will clearly give the error).

[top]

I'm getting a "ChainedRuntimeException: Failed to get the test results" error message. What does it mean?

When I run my Cactus test, I'm getting the following:

		org.apache.cactus.util.ChainedRuntimeException: 
    		Failed to get the test results [...]
	

It means that Cactus failed to retrieve the test result from the server side for some reason. To easiest way to diagnosis the problem is to enable Cactus logging(see the Configuration Howto or how to do this). The cactus client log (cactus_client.log will clearly give the error).

[top]

How to send multipart/form-data?

How to send multipart/form-data to my servlet under test?

Use a recent version of Commons-httpclient, which supports sending multipart data. Tobias also has a nice blog entry describing how to use Java NIO, Commons-httpclient and Cactus to upload a file.

Another solution is to use the HTTPClient classes from Ronald Tschalar, as demonstrated in the following example:

		import HTTPClient.Codecs;
		import HTTPClient.NVPair;
		.
		.
		.
		NVPair[] hdrs = new NVPair[1];
		
		public void beginUpload(WebRequest theRequest)
		{
		NVPair[] opts = { new NVPair("option", "myoption") };
		NVPair[] file = { new NVPair("comment", "/home/alan/src.zip") };
		try {
		byte[] data = Codecs.mpFormDataEncode(opts, file, hdrs);
		InputStream i = new ByteArrayInputStream(data);
		theRequest.setUserData(i);
		theRequest.setContentType(hdrs[0].getValue());
		i.close();
		} catch (IOException e) {
		System.out.println("Error Building Multipart");
		}
		}
	
[top]

I get a "java.lang.ClassCastException: org.apache.commons.logging.impl.LogFactoryImpl" error when I use a JUnit TestRunner different from TextTestRunner

Cactus tests run fine when I use the JUnit TextTestRunner but fails with other runners like the SwingTestRunner. I get the following exception:

		org.apache.commons.logging.LogConfigurationException:
		java.lang.ClassCastException: org.apache.commons.logging.impl.LogFactoryImpl
		at org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:505)
		at org.apache.commons.logging.LogFactory.getFactory(LogFactory.java:340)
		at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:381) 	at
		org.apache.cactus.AbstractTestCase.runBare(AbstractTestCase.java:188) 
	

Check the JUnit FAQ. Indeed, JUnit uses a special class loader to load classes that is incompatible with the Commons-Logging framework. Thus you need to put the commons-logging package in the JUnit excluded.properties file (see the JUnit FAQ).

[top]

How can I parametrize my Cactus tests?

I would like to pass some configuration information to my Cactus tests but without having to hardcode them in my tests. Can I do this, and how?

The easiest solution is to pass dynamic configuration information as System properties to Cactus. For example, if you have to pass a username and password as HTTP parameter to your servletfor all your tests and you cannot control your environment by defining a test user, you could write the following:

		public void MyTest extends ServletTestCase
		{
		public void begin(WebRequest request)
		{
			request.addParameter("username", System.getProperty("username"));
			request.addParameter("password", System.getProperty("password"));
		}
		[...]
		}
	

In order to pass the system properties to cactus you have 2 options:

  1. Pass them on the command line that you use to start the JUnit Test Runner. If you're using the ServletTestRunner, then that would be on the command line that you use to start your container.
  2. Add these properties in the cactus.properties file. All properties found in this file are imported as System Properties during the Cactus initialization.
[top]

How do I maintain a Session between 2 Cactus test?

I would like to use the same HTTP Session between several Cactus tests. How can I do that?

Cactus is a unit testing framework and not a functional testing framework. Each test must be independent of the others. There are several reasons for this: tests become very brittle, test order is not guaranteed by the JVM, etc. Thus, before each test you must set the fixture. This can be factorized in the setUp() method or in the Cactus begin() method.

[top]

What version of Eclipse do I need for the Eclipse plugin?

The Cactus plugin for Eclipse requires Eclipse 2.1 RC2 or greater. It has not yet been tested with Eclipse 3.0.

[top]

Why do I get an HttpClient NoSuchMethodError when running Cactus tests with JBoss 3.x?

When running my Cactus tests with JBoss 3.x, I get:

		java.lang.NoSuchMethodError: 
		org.apache.commons.httpclient.methods.GetMethod.setRequestHeader(
		Ljava/lang/String;Ljava/lang/String;)V at [...]
	

JBoss 3.x bundles an old version of HttpClient and Cactus requires a newer version. Just replace the HttpClient version in JBoss by the one provided by Cactus.

[top]

Does Cactus work with load-balancers?

Answer: No. For each test, Cactus performs 2 HTTP connections to the Server side: one for executing the test and one to get the test result. The test result is stored in the application scope (Servlet context). Thus, if the load-balancer directs the second HTTP connection to another server instance, the test result will not be correct.

[top]

Does Cactus supports asserting HTML pages with Javascript?

Cactus provides powerful response assertion through its integration with HttpUnit. HttpUnit does support asserting page containing Javascript. However, the Cactus integration with HttpUnit currently does not support this use case (There's a bug opened for adding this support).

[top]

I have problems using StrutsTestCase...

StrutsTestCase is a project separate from Cactus. It has its own mailing list/forum. Please post your questions there.

[top]

Does Cactus support HTTPS?

No, Cactus doesn't support HTTPS. The reason is that we don't think it's necessary. Whether you use HTTP or HTTPS should be transparent to the tested code. And you should run the tests in a test environment anyway, which doesn't necessarily need to be HTTPS just because the production environment is. Of course, if you have good arguments for HTTPS support and a patch, we might change our collective mind :-)

[top]

Why is my ServletContext and ServletConfig null in the servlet under test?

You must initialize the servlet with the implicit cactus config object (see the ServletTestCase principles documentation for more details). For example:

		MyServlet servlet = new MyServlet();
		servlet.init(config);
	
[top]