News
Features
Changes
Todo
Contributors

Downloads

Installation
Configuration
Using
Servlet Sample
FAQ

Architecture

Ant integration
Servlet Engines

API Reference

Support
Statistics


Assumptions that lead to the current J2EEUnit design

Here are some assumptions that I have made for the design of J2EEUnit :

Assumptions  
Assumption 1   The tests need to run on the server side. Indeed in a component model, the components have a closed relationship with their container : they benefit from the container's services, ... and thus they cannot easily be run outside the container. Simulating a container is too much work and leads to building a complete new container if all features need to be supported.  
Assumption 2   We need to be able to launch the tests that are located on the server side. The solution is to use a proxy class that is located on the server side. There are 2 possible implementations : (1) use a servlet which starts the tests or (2) use an EJB. I have chosen a servlet because it is the simplest solution and not all servlet engine also support EJBs.  
Assumption 3   We need to call this servlet. Again 2 solutions : (1) open a browser and call the servlet URL or (2) write some java code that uses the URLConnection class to automatically call the servlet. This is what I call the client side part of J2EEUnit. Of course the best solution is (2) for the following reasons :
- The tests can be automated, i.e. it is possible to do automatic regression tests. See the eXtreme Programming philosophy for better understanding this. The rationale is : everytime you modify some piece of code, you run all the tests again to make sure everything is still working.
- It is possible to verify the results returned by the Servlet : cookies, headers, ... By calling directly the Servlet URL in a browser, it is not possible to do so.  
Assumption 4   In order to formalize test suites and have a sexy GUI interface there were 2 solutions : (1) make my own framework and GUI or (2) use a well-known, everywhere used framework. Of course, I have chosen this second solution : all the tests are started using JUnit.  

How it works

J2EEUnit Architecture

The process is as follows for each XXX test method of your TestYYY test class :

  1. JUnit calls your the TestYYY.runTest() method (inherited from ServletTestCase). This later looks for a beginXXX(ServletTestRequest) method. If one is found, it executes it. This is executed on the client side (i.e. not in a server engine). The ServletTestRequest parameter passed to the beginXXX() method is used to set the HTTP headers, the HTTP parameters, ... that will be sent in step 2 to the Redirector proxy.
  2. The TestYYY.runTest() method then opens an HTTP connection to the Redirector proxy. All the parameters set up in the beginXXX() method are put in the HTTP request (HTTP headers, HTTP parameters, ...)
  3. The Redirector proxy acts as a proxy for your TestYYY test class, but on the server side. It means that TestYYY is instantiated twice : once on the client side and once on the server side. The client side instance is used for executing the beginXXX() and endXXX() methods (see steps 1 and 8) and the server side instance is used for executing the testXXX() methods (see step 4). The Redirector proxy does the following :
    • creates an instance of TestYYY using reflection. It sets by reflection the J2EEUnit implicit objects (see the section below on Servlet Redriector Proxy and JSP Redirector Proxy for a list of available objects).
    • creates instances of J2EEUnit wrappers for some server objects (HttpServletRequest, ServletConfig, ServletContext, ...). This is to be able to to override some methods in order to return simulated values. For example, the J2EEUnit framework can simulate an URI (i.e. act as if this URI was called instead of the Redirector proxy URI). Thus, the getServerName(), getServerPort(), getRequestURI(), ... methods return values based on the simulated URI (if there is any defined by the user).
    • creates an HTTP Session if the user has expressed the wish (using the ServletTestRequest.setAutomaticSession(boolean) code in the beingXXX() method. By default a session is always created) and it fills by reflection the session implicit object.
  4. The TestYYY.setUp(), TestYYY.testXXX() and tearDown() methods are executed (in that order). They are called by the Redirector proxy using reflection.
  5. Your TestYYY.testXXX() method calls your server side code, executing the test and using the JUnit asserts to assert the result (assert(), assertEquals(), fail(), ...)
  6. If the test fails, your TestYYY.testXXX() methods throws exceptions to the Redirector proxy.
  7. If an exception has been raised, the Redirector proxy returns the information about the exception (it's name, class, stack trace) back to the client side. It will then be printed by JUnit in it's Test Runner console.
  8. If no exception occurred, the TestYYY.runTest() method looks for an endXXX(HttpURLConnection) method and executes it if found. At this stage, you have the opportunity to check returned HTTP headers, Cookies and the servlet output stream in the endXXX() method, again using JUnit asserts and helper utility classes provided by J2EEUnit (see the sample application).
Redirector Proxies

J2EEUnit provides 2 implementation for the Redirector Proxy :

  • A Servlet Redirector. This redirector is a servlet that should be used for unit testing servlet methods. It provides the following implicit objects : request, response, session and config.
  • A JSP Redirector. This redirector is a JSP page that should be used for unit testing server code that need access to the following objects : pageContext and out. These objects are provided in addition to all the objects provided by the Servlet Redirector. It can be useful for testing simple JSP custom Tag libraries.

Note Testing custom JSP Tag libraries is still in beta. I am still not sure of the usefulness of this ... I think unit testing tag libraries is much less relevant than doing functional tests, but this is an open subject ...

Servlet Redirector Proxy

Servlet Redirector Architecture

The client side opens 2 HTTP connections to the Servlet redirector. Once to execute the tests and retrieve the servlet output stream and a second time to get the test result. This is to be able to get the exception data (message, stack trace, ...) if the test failed. The test results are stored in a servlet-context-wide scope variable which is retrieved on the second HTTP connection.


JSP Redirector Proxy

JSP Redirector Architecture

The client side opens 2 HTTP connections. Once to the Redirector JSP to execute the tests and retrieve the JSP output stream and a second time to the Servlet Redirector to get the test result. This is to be able to get the exception data (message, stack trace, ...) if the test failed. The test results are stored in a servlet-context-wide scope variable which is retrieved on the second HTTP connection.





Copyright © 2000-2001 Vincent Massol. All Rights Reserved.