Running Fitnesse inside the container
How does fitnesse work currently?
When you click the test button on the fitnesse page, the following steps occur
1. The TestResponder class gets invoked
a. Based on the !path variable defined on the root page, it builds the classpath
b. It uses the TEST_RUNNER variable to figure out the class that needs to be invoked to run the fit tests. It defaults to fit.FitServer
c. Spawns a new thread and invokes the fit.FitServer class in a new java process with the classpath it just built. It passes 3 parameters to this class.
c1. Host on which fitnesse is running.
c2. Port on which fitnesse is running.
c3. Socket token or id, to identify
d. Declares itself as the socket listener and waits for socket connection from fit.FitServer class based on the socket token
2. Inside the FitServer class,
a. it establishes a socket connection with the fitnesse server based on the host and port given to it
b. On this socket connection, it makes a http request as follows “GET /?responder=socketCatcher&ticket=“+socketToken+“ HTTP/1.1\r\n\r\n”
c. This will help to connect back to the TestResponder class, which then returns the fit test page [html] as a string
d. It creates a parse object from this string and invokes the doTable() on a new fit fixture. Eg: newFixture().doTables(tables);
e. It then publishes the results back on the wiki page thru the TestResponder
3. The TestResponder class waits on the thread in which it invoked the FitServer class. Once the FitServer class has terminated, it does all the publishing stuff and stops.
Issues with this approach:
1. EJB Refs won't work : If you are trying to tests EJBs which are been looked up using EJB Refs, then they won‘t work. Let‘s say you are doing your EJB lookup inside your business delegate classes. In this case, you would write a fixture class which calls the business delegate, which in turn calls your EJB. But your fixture classes and business delegate classes are running in a new JVM [FitServer space] and they cannot resolve the EJB refs as they are not running inside the container [Application server space]
2. Remote debugging won't work : If your fit tests are testing some J2EE components, it is not possible to use the remote debugging options provided by the IDEs coz we have 3 different JVMs involved. [fitnesse, FitServer, Application Server]. Also you need to look at 2 different logs for trace statements. So debugging gets very tricky.
3. Difference in test and production environment : In production env, you would have a web front end running inside a web container which calls the EJBs residing inside the EJB container thru the Business Delegates. Hence you would have the EJBs being invoked from the web container and you would probably want something similar in your fit tests. You don‘t want EJBs been called directly from outside the container.
4. Managing classpath mess : Defining the classpaths on the .root page can get very cryptic and error prone. Development teams tend to waste a lot of time setting up classpath and maintaining it.
5. Setting up and maintaining another environment : If you trying to run tests from Fitnesse, then you need to configure and maintain a lot of other stuff for your enterprise app to work properly. I have seen a lot of teams struggling with properties, log files, security, etc
Solution:
We want the FitServer class to be invoked inside the web container. Then we can setup remote debugging on the web container and easily debug the fixture, business delegate and actual EJB code. This also helps us to avail all the EJB ref look ups and other container provided services.
Implementation:
1. Add the following line at the parent fit test page.
!define TEST_RUNNER {fit.FitServerServletInvoker} : This will tell fitnesse [TestResponder class] to invoke fit.FitServerServletInvoker class instead of fit.FitServer
!define COMMAND_PATTERN {java -cp %p %m http://localhost:8080/yourAppName/FitServlet} : This helps us pass the URL of the fit servlet that needs to be invoked
2. Write the fit. FitServerServletInvoker class and make sure you put it in fitnesse path. [Add it to the !path property on the root page]
3. All the fit. FitServerServletInvoker class has to do is, make an HttpURLConnection to your web container, call the appropriate Servlet on the container and pass all the arguments it got. [3 parameters: host, port and socketToken]
4. Write the Servlet which invokes the FitServer class with the parameters it‘s got. I have called the Servlet as FitServlet.
5. Add your servlet definition to the deployment descriptor of your web container [web.xml]
I have also modified this servlet to use the TestRunner class instead of the FitServer if the socket token is not present. This servlet is used to run my fit tests inside the container from directly the web browser or from the ant build file. [We run some fit tests as a part of our continuous integration build]
All the source code and related files is now open sourced. Please refer to the Patang project for details.