Thomas Sundberg

November 16, 2011

Testing a Web Service with SoapUI, JUnit, Maven and Cucumber

An example is perhaps the best way to describe something. Concrete examples are easier to understand then abstract descriptions.

I will show how SoapUI can be used to test a web service. I will also show three different tools that can be used to control SoapUI. This tool chain can easily be made a part of your continuous build.

The example

The example I will use is about car maintenance. A car with an empty fuel tank need to be refueled. The car exists behind a web service with three methods defined using the Web Service Definition Language, WSDL, below.

The WSDL

<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
             xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
             xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://example.sigma.se/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"
             targetNamespace="http://example.sigma.se/" name="CarService">
    <types>
        <xsd:schema>
            <xsd:import namespace="http://example.sigma.se/" schemaLocation="http://localhost:8090/car?xsd=1"/>
        </xsd:schema>
    </types>
    <message name="addFuel">
        <part name="parameters" element="tns:addFuel"/>
    </message>
    <message name="addFuelResponse">
        <part name="parameters" element="tns:addFuelResponse"/>
    </message>
    <message name="getFuelLevel">
        <part name="parameters" element="tns:getFuelLevel"/>
    </message>
    <message name="getFuelLevelResponse">
        <part name="parameters" element="tns:getFuelLevelResponse"/>
    </message>
    <message name="emptyFuel">
        <part name="parameters" element="tns:emptyFuel"/>
    </message>
    <message name="emptyFuelResponse">
        <part name="parameters" element="tns:emptyFuelResponse"/>
    </message>
    <portType name="Car">
        <operation name="addFuel">
            <input wsam:Action="http://example.sigma.se/Car/addFuelRequest" message="tns:addFuel"/>
            <output wsam:Action="http://example.sigma.se/Car/addFuelResponse" message="tns:addFuelResponse"/>
        </operation>
        <operation name="getFuelLevel">
            <input wsam:Action="http://example.sigma.se/Car/getFuelLevelRequest" message="tns:getFuelLevel"/>
            <output wsam:Action="http://example.sigma.se/Car/getFuelLevelResponse" message="tns:getFuelLevelResponse"/>
        </operation>
        <operation name="emptyFuel">
            <input wsam:Action="http://example.sigma.se/Car/emptyFuelRequest" message="tns:emptyFuel"/>
            <output wsam:Action="http://example.sigma.se/Car/emptyFuelResponse" message="tns:emptyFuelResponse"/>
        </operation>
    </portType>
    <binding name="CarPortBinding" type="tns:Car">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <operation name="addFuel">
            <soap:operation soapAction=""/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
        <operation name="getFuelLevel">
            <soap:operation soapAction=""/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
        <operation name="emptyFuel">
            <soap:operation soapAction=""/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    <service name="CarService">
        <port name="CarPort" binding="tns:CarPortBinding">
            <soap:address location="http://localhost:8090/car"/>
        </port>
    </service>
</definitions>

We can see that there are three methods available here

  • addFuel
  • getFuelLevel
  • emptyFuel

This a really boring example, you can add fuel, check the level and empty the tank. But it is sufficient complicated so you can get a feeling that the example actually does something. We will not use the WSDL, it is presented just so you can see the definition and be glad that you don’t have to penetrate everything to understand the example.

The file structure

Before you start building the example, I need to show the file structure this example lives in.

    example --- product --- src -- main -- java -- se -- sigma -- example --- Car.java
             |           |                                                 |
             |           |                                                 -- WebService.java
             |           |
             |           |
             |           -- pom.xml
             |
             |
             -- test --- src -- test -- java -- se -- sigma -- example --- FuelCarTest.java
             |        |                   |                             |
             |        |                   |                             -- FuelCarSteps.java
             |        |                   |
             |        |                   -- resources -- se -- sigma -- example -- CarMaintenance.feature
             |        |
             |        -- pom.xml
             |
             |
             -- pom.xml

Given this file structure, you should be able to re-create this example.

The Web Service

We have seen the WSDL for the web service that will be used in the example. The actual implementation is done using two classes, Car and WebService. Car is the domain logic and WebService is the provider that will make Car available.

The Car implementation:

File: product/src/main/java/se/sigma/example/Car.java

package se.sigma.example;

import javax.jws.WebMethod;
import javax.jws.WebService;
import java.util.Date;

@WebService
public class Car {
    private Integer fuelLevel;

    public Car() {
        fuelLevel = 0;
    }

    @WebMethod
    public void addFuel(int addedAmount) {
        String message = "adding " + addedAmount;
        usageLog(message);
        fuelLevel = fuelLevel + addedAmount;
    }

    @WebMethod
    public Integer getFuelLevel() {
        String message = "returning fuel level " + fuelLevel;
        usageLog(message);
        return fuelLevel;
    }

    @WebMethod
    public void emptyFuel() {
        String message = "Emptying fuel tank";
        usageLog(message);
        fuelLevel = 0;
    }

    private void usageLog(String message) {
        Date now = new Date();
        System.out.println(now + " " + message);
    }
}

This is nothing more then a pojo, plain old java object, with some annotations.

The simplest possible thing that could work for providing this as a web service might be to use the javax.xml.ws.Endpoint class as the publishing tool. It will take the annotated class make it available as a web service. The implementation I use looks like this:

File: product/src/main/java/se/sigma/example/WebService.java

package se.sigma.example;

import javax.xml.ws.Endpoint;

public class WebService {
    public static void main(String[] args) {
        Endpoint.publish("http://localhost:8090/car", new Car());
    }
}

The service will be published behind port 8090 and the context car. You should be able to access it from http://localhost:8090/car

The final part that is needed to tie this example together is a Maven pom. The one I used here looks like:

File: product/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>se.sigma.cucumber</groupId>
        <artifactId>example</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>product</artifactId>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-rt</artifactId>
            <version>2.2.5</version>
        </dependency>
    </dependencies>
</project>

This simple implementation created the WSDL above. The next step is to create a SoapUI project that can be used to explore and test the service.

The rest of this example assumes that the main defined above in the WebService is running. Start it from your IDE or whatever tool you use to edit Java. I started it from the tool of my choice, IntelliJ IDEA.

SoapUI

The star in this example is SoapUI. SoapUI is an open source testing tool that will allow you to explore a Web Service just by examining the WSDL. An introduction with some images can be found at http://www.soapui.org I will limit my self to explain the steps brief in text.

  • Get started
    • Start SoapUI
    • Create a new Workspace, I called mine Agile Testing Days
  • New project
    • Create a new SoapUI project – I called mine CarMaintenance
    • In the dialog, enter the path to the wsdl file.
    • I entered http://localhost:8090/car since I have the service running
    • I left the Create Requests ticked
    • and pressed OK and my project was created with samples for all three requests
  • Add a test suite
    • Right click on the CarMaintenance project and add choose New TestSuite
    • I called it CarMaintenanceSuite
  • Add a test case
    • Right click on the test suite and add a New TestCase
    • I called it CarMaintenanceTestCase
  • Add a test step
    • Right click on the test case and Add Step of the type Test Request
    • I called it EmptyTank and selected the method to be CarPortBinding -> emptyFuel
    • You should have a sample request in the workspace now
    • Click on the small green arrow at the top left corner and a request should be sent to the service
    • You should see that the response was ok, the Assertions bullet in the lower left corner is green
  • More test steps
    • I want to add two more steps so we can explore verification of the response
    • Right click on Test Steps and Add Step of the type Test Request
    • I call is AddFuel
    • and select that the request CarPortBinding -> addFuel
    • Verify that Create optional elements checkbox is ticked
    • A sample request shows up in the working area
  • Define the add fuel property
    • The property has to be defined so we can change it later
    • Click on the small dots in the upper, left corner of SoapUI, just above the Workspace name
    • Set the properties on the project level
    • Right Click on the properties just below the project name
    • add a property called addedFuel
    • Expand the properties
    • Right click on the property addedFuel
    • Set it’s value to 17
  • Use the add fuel property
    • An optional element arg0 is available in the request and it is marked with a ?
    • Change the ? to a property so we can modify it later
    • I called my property addedFuel and the value that should replace the ? is
    • ${#Project#addedFuel}
    • The line should now read
    • <arg0>${#Project#addedFuel}</arg0>
  • Verify the script so far
    • Test the new request by clicking the green arrow again
    • Verify that a log statement was printed in the console for the service
    • Also note the green Assertions bullet at the lower left corner of the step
  • Add a final test step
    • Lets add a final step so we have a complete test cycle to play with
    • Right click on Test Steps and Add Step of the type Test Request
    • I call is VerifyFuelLevel
    • and select that the request CarPortBinding -> getFuelLevel
  • Verify the last step
    • Verify that it works by clicking the green arrow in the sample request
    • The Assertions bullet went green and we can see that in the response
    • and we have a row >return<17>/return<
  • Add an real assertion
    • The last thing we want to add is a verification that we actually get the same value back as we just set
    • Click on the small + sign next to the green arrow
    • Select to add an assertion of the type Contains
    • I added the value
    • ${#Project#expectedFuel}
    • that should match the property expectedFuel we are using to vary our parameters
  • Define the property for the assertion
    • Right Click on the properties just below the project name
    • add a property called expectedFuel
    • Expand the properties
    • Right click on the property expectedFuel
    • Set it’s value to 17
  • Verify the assertion property
    • Run the last step and verify that the value actually is used
    • Change the expectedFuel to 42 and re-run the step and see that the assertions are failing
    • Set the property back to 17 and re-run again to make sure that the assert works as expected.
  • Save the script
    • Save the project from the File menu, Save All Projects

Now we have a working SoapUI project. We could stay here. But that would mean that somebody would have to run the test manually. We would rather have a build system or similar to run the test often. Lets use the SoapUI project and connect to three different tools. These tools are:

  • Maven
  • JUnit
  • Cucumber

Maven

Maven is a build tool that many people has opinions about, either they hate it or they love it. I will show how the Maven SoapUI plugin can be configured to run the project we just set up.

We need to define a plugin repository, SoapUI isn’t available on Maven Central. It should be http://www.eviware.com/repository/maven2/

<pluginRepositories>
    <pluginRepository>
        <id>eviwarePluginRepository</id>
        <url>http://www.eviware.com/repository/maven2/</url>
    </pluginRepository>
</pluginRepositories>

The plugin also has to be configured

<plugin>
    <groupId>eviware</groupId>
    <artifactId>maven-soapui-plugin</artifactId>
    <version>4.0.1</version>
    <configuration>
        <projectFile>test/src/test/soapUI/CarMaintenance-soapui-project.xml</projectFile>
        <outputFolder>./test/target/soapUI</outputFolder>
        <junitReport>true</junitReport>
        <printReport>true</printReport>
        <projectProperties>
            <value>addedFuel=17</value>
            <value>expectedFuel=17</value>
        </projectProperties>
    </configuration>
    <executions>
        <execution>
            <phase>integration-test</phase>
            <goals>
                <goal>test</goal>
            </goals>
        </execution>
    </executions>
</plugin>

The most important things here are of course the parameters that we may want to vary and the path to the SoapUI script.

Note the syntax for defining the parameters, you have to have the name followed by an equal sign and then the value. There may not be any spaces.

The complete Maven pom will be available below.

JUnit

Another option for testing web services through SoapUI is to connect to it from JUnit. This would eliminate the usage of the Maven Plugin. An example is the JUnit implementation below:

File: test/src/test/java/se/sigma/example/junit/FuelCarTest.java

package se.sigma.example.junit;

import com.eviware.soapui.tools.SoapUITestCaseRunner;
import org.junit.Test;

public class FuelCarTest {
    @Test
    public void verifyTheInputValueIsReturned() throws Exception {
        SoapUITestCaseRunner runner = new SoapUITestCaseRunner();
        runner.setProjectFile("/Users/tsu/Dropbox/projects/tsu/blog/soapUI-junit-maven-cucumber/example/test/src/test/soapUI/CarMaintenance-soapui-project.xml");
        String[] properties = new String[2];
        properties[0] = "addedFuel=42";
        properties[1] = "expectedFuel=42";
        runner.setProjectProperties(properties);
        runner.run();
    }
}

The parameters are set using the same syntax as in the Maven plugin. They are defined in the array properties and passed to the script. The path to the SoapUI script is defined as an absolute path, a relative path resulted in problems for the SoapUITestCaseRunner to locate the script.

Both the Maven plugin and the JUnit implementation has problems with it descriptions. It is not easy to look at the Maven plugin and decide what this test actually does. Similar, it is not very easy to look at the JUnit implementation and see what actually is going on here. This may be corrected with the next tool, Cucumber.

Cucumber

Cucumber is a Behaviour Driven Development, BDD, tool that allows you to define what you expect with the syntax

  • Given
  • When
  • Then

This example is then translated to executable code through some step definitions. The steps are not meant for somebody unfamiliar with code to read. They should read a feature that defines what we expect, other has to implement and read the steps that actually uses the system under test.

A feature that defines what we want to verify may look like:

File: test/src/test/resources/se.sigma.example.cucumber/CarMaintenance.feature

Feature: Daily car maintenance
  Cars need maintenance


Scenario: Fuelling
    Given a car with an empty gas tank
    When you fill it with 50 litres of fuel
    Then the tank contains 50 litres

This feature cannot live by itself, it need support. The most important thing is to define the steps that actually connects to the Web Service. One implementation may look like:

File: test/src/test/java/se/sigma/example/cucumber/FuelCarSteps.java

package se.sigma.example.cucumber;

import com.eviware.soapui.tools.SoapUITestCaseRunner;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;

public class FuelCarSteps {
    private String[] properties = new String[2];

    @Given("^a car with an empty gas tank$")
    public void a_car_with_an_empty_gas_tank() {
        // Nothing to do here, it will be taken care of in the SoapUI script
    }

    @When("^you fill it with (.*) litres of fuel$")
    public void you_fill_it_with_litres_of_fuel(String addedFuel) {
        properties[0] = "addedFuel=" + addedFuel;
    }

    @Then("^the tank contains (.*) litres$")
    public void the_tank_contains_litres(String expectedFuel) throws Exception {
        properties[1] = "expectedFuel=" + expectedFuel;

        SoapUITestCaseRunner runner = new SoapUITestCaseRunner();
        runner.setProjectFile("/Users/tsu/Dropbox/projects/tsu/blog/soapUI-junit-maven-cucumber/example/test/src/test/soapUI/CarMaintenance-soapui-project.xml");
        runner.setProjectProperties(properties);
        runner.run();
    }
}

The Given and When steps doesn’t really do anything else than read some parameters and store them so they are available in the final Then step. It is of course the Then step that actually performs anything.

The steps need to be glued together with the steps. It can be done using a JUnit runner. It is implemented as:

File: test/src/test/java/se/sigma/example/cucumber/FuelCarTest.java

package se.sigma.example.cucumber;

import cucumber.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
public class FuelCarTest {
}

Note that the steps may not be defined in the test class. Steps are defined globally and defining them in the test class would tie them hard to this particular feature.

Maven revisited

A Maven pom that supports all three tools at the same time may look like this:

File: test/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>se.sigma.cucumber</groupId>
        <artifactId>example</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>test</artifactId>
    <repositories>
        <repository>
            <id>soapUI</id>
            <url>http://www.eviware.com/repository/maven2/</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>eviwarePluginRepository</id>
            <url>http://www.eviware.com/repository/maven2/</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>eviware</groupId>
                <artifactId>maven-soapui-plugin</artifactId>
                <version>4.0.1</version>
                <configuration>
                    <projectFile>./example/test/src/test/soapUI/CarMaintenance-soapui-project.xml</projectFile>
                    <outputFolder>./test/target/soapUI</outputFolder>
                    <junitReport>true</junitReport>
                    <printReport>true</printReport>
                    <projectProperties>
                        <value>addedFuel=17</value>
                        <value>expectedFuel=17</value>
                    </projectProperties>
                </configuration>
                <executions>
                    <execution>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>se.sigma.cucumber</groupId>
            <artifactId>product</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>eviware</groupId>
            <artifactId>maven-soapui-plugin</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>1.0.1</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>1.0.1</version>
        </dependency>
    </dependencies>
</project>

Conclusion

It is not very difficult to verify a web service. We need some tools and we need to connect them properly. I choosed to use Cucumber to increase the readability in this example. If you prefer using the Maven plugin or JUnit, please do so.

Acknowledgements

This post has been reviewed by some people who I wish to thank for their help

  • Malin Ekholm
  • Johan Karlsson
  • Johan Helmfrid

Thank you very much for your feedback!

Resources

About these ads

10 Comments »

  1. You have not posted the root pom.xml file.

    Comment by Lanoxx — December 13, 2012 @ 12:24

    • No, I seem to have missed it.

      The root pom is an aggregation pom that contains the two modules product and test, some common properties and a common dependency like this:

      <?xml version="1.0" encoding="UTF-8"?>
      <project>
          <modelVersion>4.0.0</modelVersion>
          <groupId>se.sigma.cucumber</groupId>
          <artifactId>example</artifactId>
          <version>1.0-SNAPSHOT</version>
          <packaging>pom</packaging>
          <modules>
              <module>product</module>
              <module>test</module>
          </modules>
          <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
              <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
          </properties>
          <dependencies>
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.10</version>
                  <scope>test</scope>
              </dependency>
          </dependencies>
      </project>
      

      Comment by Thomas Sundberg — December 14, 2012 @ 07:02

  2. Instead of Junit, I just want to run soapui tests from ruby code, almost equal to cucumber(Features)-ruby(Step definitions in ruby)-soapui(to run soap tests), but I didn’t find any ruby support to kickoff soapui tests like SoapUITestCaseRunner.

    Comment by Ram — January 4, 2013 @ 00:16

    • I mean to say I just want to have support file in ruby instead of java

      Comment by Ram — January 4, 2013 @ 00:17

      • I am sorry, I haven’t tried to use that combination. I would ask the guys at Smartbear, who is developing SopaUI, if they have any pointers.

        HTH
        Thomas

        Comment by Thomas Sundberg — January 4, 2013 @ 06:20

  3. Wonderful post !! really helped me, Thanks.

    Comment by Sunil — January 4, 2013 @ 08:30

  4. [...] Testing a Web Service with SoapUI, JUnit, Maven and Cucumber | Thomas Sundberg – March 19th ( tags: soapUI junit maven cucumber testing howto example tutorial guide example test webservices ) [...]

    Pingback by Delicious Bookmarks for March 19th from 15:03 to 22:00 « Lâmôlabs — March 20, 2013 @ 03:01

  5. Thanks for nice post. Could you please elaborate SoapUITestCaseRunner class and its definition. how it is being used in cucumber then steps.

    Comment by Samiran Roy — September 11, 2014 @ 09:23

    • Hi!

      What is it that you would like to know about SoapUITestCaseRunner? It is a way to execute a SoapUI project. What is it above that you need more information about? What didn’t work when you tried to use it?

      /Thomas

      Comment by Thomas Sundberg — September 11, 2014 @ 20:42


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Silver is the New Black Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 57 other followers

%d bloggers like this: