sorenpoulsen.com header

Mock Services For Maven Integration Tests With The SOAPUI Plugin

SoapUI has this nifty little Maven plugin that is capable of running mock services designed with SoapUI during Maven's test lifecycle phase. This enables us to build Maven integration tests that call real services running on a network address and to marshall/unmarshall real XML requests and responses.

This post walks through the highlights to set up a Maven integration test project that utilize the SoapUI Maven plugin. The full project can be cloned from GitHub, linked in the last section.

screenshot of SoapUI 5.4.0 mock service project

In a real enterprise, services often have big and complex responses that can exercise different code paths in the service consumer depending on the data in the response. For our service mock to be of any real value, it has to match different request payloads on the same service operation to different responses, so that we get to test all possible code paths. Although the service we will use for this project is quite simple, we will demonstrate SoapUI's QUERY-MATCH feature, that enable us to return different responses from the same service operation.

The Maven integration project will consist of two modules, one named "ServiceResources" that generate the JAX-WS service client artifacts from a WSDL file, and another named "ServiceClient" that contain the actual integration test, which is run with JUnit and the soapui-maven-plugin.

Introducing the service

The following WSDL file is used to generate the JAX-WS service client and the SoapUI mock service. It describes a simple Document/Literal service with a single operation called SendMessage, that sends a String message and returns a String response.

The file is stored in the Maven project under ServiceResources/src/main/resources/test.wsdl

A typical request for this service would look like this:

And a typical response:

Create a JAX-WS service client with wsimport

Wsimport is a tool bundled with the JDK that can be used to create a Java service client from a WSDL file. We are not using the tool directly, instead we use the "wsimport" goal of the jaxws-maven-plugin to create the client in Maven's "generate-sources" phase.

What we end up with is a JAX-WS Service and Port class and a number of JAXB classes that are used at runtime to marshall and unmarshall between the XML messages and their Java class representation.

The jaxws-maven-plugin is configured in the pom.xml file of the ServiceResources module. We dont' have to configure the plugin a whole lot but here's a few points:

  • The "wsdlDirectory" parameter points to the WSDL file from which the client is generated.
  • At runtime we want JAX-WS to read the WSDL file from the Jar file rather than from a network address. This is why the WSDL file is stored in the src/main/resources folder that Maven packs into into the Jar file. We use the plugins configuration parameter "wsdlLocation" to make sure the generated Service class loads the WSDL from the classpath.
  • The "extension" parameter tells wsimport that it's OK to use SOAP version 1.2.

Creating a mock service with SoapUI

Start SoapUI 5.4.0 and select File->New SOAP Project. Point "Initial WSDL" to ServiceResources/src/test/resources/test.wsdl. Make sure to check "Relative Paths".

screenshot of SoapUI 5.4.0 to create a new projectt

Once the project is created right-click the project and select New SOAP Mockservice. Name it "testmock".

Right-click the new test mock and select New MockOperation and then select the SendMessage operation from the dropdown.

We are going to create two mock responses for the SendMessage operation. One that returns the response message "triggered" when the mock service receives a request with the message "hello" and another that returns the response message "Default". The latter will be returned on any other request message than "hello".

Since SoapUI already created a "Response 1" for the SendMessage operation, we will just customize that response a bit. Right-click "Response 1" and select Rename. Name it "Triggered response". Change the response XML body to return the response string "triggered". It should look like this:

Create another mock response by right-clicking the SendMessage operation and selecting "New MockResponse". Name it "Default response" and change its response XML body to return the response string "Default":

Now double-click the SendMessage operation under the test mock.

In the Default Response dropdown menu select the response we named "Default response".

screenshot of SoapUI 5.4.0 query-match

In the "dispatch" dropdown menu select QUERY_MATCH. Click the green plus sign to add a new query match. Name it "message". Make sure "message" is selected, then add this content to the XPATH field:

Set the "Expected value to "hello" and in the "Dispatch to" dropdown menu select the "Triggered response".

Close the SendMessage popup and and press ctrl-alt-s to save the project. Store the SoapUI project under ServiceClient/src/test/soapui/test-soapui-project.xml. It's important that the SoapUI project file is stored in the same Maven module as the JUnit test cases (we will get to them later).

Configure the SoapUI Maven plugin

The configuration of the soapui-maven-plugin is added to the pom.xml of the Maven module that also hosts the JUnit test cases for the integration test. That would be the ServiceClient module in our case, but for at layered application, split across multiple Maven modules, you would probably launch your integration tests from a higher layer such as the UI layer.

To configure the plugin, point the parameter "projectFile" to the SoapUI project XML file that we stored under ServiceClient/src/test/soapui/test-soapui-project.xml. The mock service is obviously launched on localhost, but the port and path are overridden, so that we control them, rather than using whatever was stored in the SoapUI project's XML file.

The plugin's "mock" goal is bound to Maven's "process-test-classes" phase. This is the phase just before the actual "test" phase.

Notice that the port is set to the property ${mockport}. We use a plugin called port-allocator-maven-plugin to dynamically assign a free port number to ${mockport}. Otherwise we could have build jobs, running on a build server such as Jenkins, trying to bind the mock service to the same port at the same time.

The only configuration needed for port-allocator-maven-plugin is to name the property for the port number:

Create JUnit test case

The dynamic port number assigned by the port-allocator-maven-plugin must also be available to the test code. This is done by writing the Maven build properties to a file on the classpath using the properties-maven-plugin:

The JUnit test simply reads the properties from classpath to get the mockport. Then it builds the endpoint address, calls the mock service using the generated Service, Port and JAXB classes and finally asserts that the response is as expected.

As mentioned previously, there are two test cases to demonstrate, that SoapUI can match a request to a particular mock reponse based on the content of the request. One test case calls the service with the message "hello" to test the "triggered" response and another calls with another message to test the default response.

The test class is stored under ServiceClient/src/test/java/ServiceTest.java

Running the integration test

The Maven project is available on GitHub: https://github.com/SorenPoulsen/mock-services-with-maven-soapui-plugin

These are the requirements to run the integration test:

  • Maven 3.5.0 installed and "mvn" executable on PATH.
  • JDK 1.8 installed and JAVA_HOME pointing the the root folder of the JDK.

Clone the GIT project and run Maven from the root folder of the project:

$ git clone https://github.com/SorenPoulsen/mock-services-with-maven-soapui-plugin.git
$ cd mock-services-with-maven-soapui-plugin/ServiceMock
$ mvn test

{{model.usr.name}}
{{cmt.user.name}}
{{cmt.user.name}}
{{childcmt.user.name}}
{{childcmt.user.name}}