Automated acceptance testing using Fitnesse, Java and the OSB

When I was at Xebicon this year I saw a talk from Dave Farley. At 26 minutes or so he starts talking about Domain Specific Languages. Making an abstraction layer on top of your system where your tests run against so when the underlying system changes, you only have to adjust your layer in between instead of all the tests. This idea got me thinking of how we could be able to do this as I am mainly working in the integration business building services. This lead me to Fitnesse. Fitnesse has been around for many years now but I never had the chance to do something with it. But what is Fitnesse?

fitnesse

Fitnesse is a lightweight, open-source framework that makes it easy for software teams to:

  • Collaboratively define Acceptance Tests, web pages containing simple tables of inputs and expected outputs.
  • Run those tests and see the results.

I was interested if I could make a simple abstraction from a service which I build using the OSB and got Fitnesse to run tests against it. Also I was interested if you could then possibly wire it into a build/release pipeline.

The first thing was to get a simple webservice running. I used an old 12C webservice of mine which I build in a previous post. It is a conversion rate webservices with a SOAP or REST entry. I will be using the SOAP one for now. The service takes two parameters, a from and a to currency and the fetches the conversionrate. I deployed it and tested it and as you can see, it works.

soap-service

Next is to get Fitnesse up and running. This is as simple as downloading it and running it using Java like java -jar fitnesse-standalone.jar. When it is up, you can go to http://localhost:80 to see the Fitnesse wiki startpage.

fitnesse-wiki

The next thing is to create a test page. Click the Add link and choose Test Page. Give the page a name and a description. If you start a page name with Test….Fitnesse will automatically recognize it as a test page. Save it for now because we first have to make a piece of Java which will be the abstraction layer between the tests and our OSB webservice. Open your favourite IDE and create a simple Java project based on Maven. I am going to use jax-ws to create a webservice client and write a fixture. A fixture is a Java class which can be extended so Fitnesse can inject the test variables and retrieve the results making use of reflection and such. So we need to add a Fitnesse dependency in our pom and I am going to use the jax-ws maven plugin to generate classes based on the service WSDL. You can do this in various ways but for now I am going to use plain Java. My pom looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>nl.redrock</groupId>
    <artifactId>FitnesseTest</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.fitnesse</groupId>
            <artifactId>fitnesse</artifactId>
            <version>20081201</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxws-maven-plugin</artifactId>
            <version>1.12</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- usage of jax-ws maven plugin-->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>1.12</version>
                <executions>
                    <execution>
                        <id>wsimport-from-jdk</id>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--wsdls file directory -->
                    <wsdlDirectory>src/main/resources/wsdls</wsdlDirectory>
                    <!-- which wsdl file -->
                    <wsdlFiles>
                        <wsdlFile>conversionrateservice.wsdl</wsdlFile>
                    </wsdlFiles>
                    <!-- Keep generated files -->
                    <keep>true</keep>
                    <!-- Package name -->
                    <packageName>nl.redrock.fitnessetest</packageName>
                    <!-- generated source files destination-->
                    <sourceDestDir>target/generated-code/src</sourceDestDir>
                </configuration>
            </plugin>
            <!-- adding generated source -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>target/generated-code/src</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

As you can see I refer to src/main/resources/wsdls where my service WSDL is located. I can now just run mvn generate-sources to generate the classes I need for calling the OSB service. When I ran mvn generate-sources I have the following classes:

generated-sources

I can now write the fixture and use JUnit to test it. I am going to use a simple fixture but as your can see here there are many fixtures which you can extend. Mine looks like this:

package nl.redrock.fitnessetest.fixtures;

import fit.*;
import nl.redrock.fitnessetest.ConversionRateService_Service;
import nl.redrock.fitnessetest.Currency;
import nl.redrock.fitnessetest.GetConversionRateRequest;
import nl.redrock.fitnessetest.GetConversionRateResponse;

/**
 *
 * @author Hugo
 */
public class ConversionRateServiceFixture extends ColumnFixture {
    
    public String from;
    public String to;
    
    public double getConverionRate() throws Exception
    {
        ConversionRateService_Service client = new ConversionRateService_Service();
        GetConversionRateRequest req = new GetConversionRateRequest();
        req.setFromCurrency(Currency.fromValue(this.from));
        req.setToCurrency(Currency.fromValue(this.to));
        
        GetConversionRateResponse response = client.getConversionRateServicePort().getConversionRate(req);
        return response.getConversionRateResult();
    }
    
}

As you can see it extends the class ColumnFixture and I have 2 public variables to and from.

The next thing is to write a JUnit test to see if the fixture works:

package nl.redrock.fitnessetest;

import nl.redrock.fitnessetest.fixtures.ConversionRateServiceFixture;
import org.junit.Test;

public class ConversionRateServiceTest {

    @Test
    public void ConversionRateClientTest() throws Exception {
        
        ConversionRateServiceFixture fix = new ConversionRateServiceFixture();
        fix.from="EUR";
        fix.to="USD";
        System.out.println(fix.getConverionRate());
    }
}

I have no assertion here, just a System.out to see if the service works. Lets run it and see the magic.

junit-test

Perfect. The last thing we have to do now is to package everything into a jar so we can tell Fitnesse to put it on the classpath. Just run mvn clean package and it should create your jar in the target directory. Now lets get back to Fitnesse. Open the page you have just create and edit it.

Add the following code to the wiki page:

!path D:\Workspace\Java\FitnesseTest\target\FitnesseTest-1.0.jar

!|nl.redrock.fitnessetest.fixtures.ConversionRateServiceFixture |
| to | from | getConverionRate? |
| USD | EUR | 1.111 |
| EUR | EUR | 1.0 |

The !path will tell Fitnesse that it should put this jar on its classpath when running the tests. The
!|nl.redrock.fitnessetest.fixtures.ConversionRateServiceFixture | will tell which is the fixture class. After that it is just easy. The first two columns say which parameters can be used as input, and the third on for output. Notice the questionmark at the end of getConverionRate?.
The last 2 rows are just testcases.

Now save the page and press the Test button at the top of the screen.

fitnesse-test

There we go. We ran the 2 testcases and got the results back. I checked Jenkins for plugins and there is a plugin which enables you to run the Fitnesse tests so you easily add them to release pipeline.

So

  • The abstraction layer between your service and the tests is quite a big benefit. Lets say the contract of the service has a breaking change. We add a mandatory field. Normally you would have to change all your existing testcases. With the abstraction layer you would be able to just fix this in the fixture (if your existing testcases are unaffected by the added field of course)
  • another nice feature is that anyone can make testcases without any knowledge of soap/xml/webservice. If your fixture is nicely programmed, even business people could write tests. Quite often acceptence testers are not used to testing services without a GUI. With this its possible to do that.
  • last one is that you completely automate it. Quite often acceptance tests are done by hand which take quite some time and effort. Now you automate them and reduce the time you can possibly deliver your software to production.

Some considerations though are:

  • You do need to put in the effort to make the abstraction layer. In the beginning this will take more effort but you will earn this back quite fast when you need to regression tests or so. In the long run….this will pay of!
  • Fitnesse can be used by different languages such as .Net and Java but you will still need people who are capable of this.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.