-
Book Overview & Buying
-
Table Of Contents
-
Feedback & Rating

SoapUI Cookbook
By :

This recipe shows how to generate and develop a simple RESTful web service stub test-first using TDD. The main SoapUI learning will be how to test a simple RESTful web service defined by a WADL that produces JSON responses. Basic JAX-RS web service development skills using Apache CXF can also be learned here.
The example service is a REST version of the SOAP invoice service from the first recipe. The service is defined by a WADL with the following main properties:
: invoice_v1.wadl
http://localhost:9000/invoiceservice/v1
GET /invoice/{id}
application/json
Apache CXF will be used to generate, build, and run the stub web service. See the Getting ready section in the first recipe if you need advice on how to download Apache CXF.
Eclipse users
If you are using Eclipse, you can set up Apache CXF as a runtime library that is by navigating to Project | Add Library | CXF Runtime, and run the server class as a Java application.
The invoice-v1-soapui-project.xml
project for this recipe can be found in the this chapter's sample code files.
First, we'll create a REST project from the service's WADL, and add a TestStep
with Assertions
to check whether the response's invoice values are what we expect. Then, we'll generate an empty runnable REST web service using Apache CXF, and finally add a simple implementation to pass the test. Perform the following steps:
invoice_v1.wadl
. Go to File Menu | New REST Project | Import WADL, browse to invoice_v1.wadl
, and click on OK. This should generate a project with a sample request to the invoice resource that takes an id
path parameter, that is, http://localhost:9000/invoiceservice/v1/invoice/{id}
.TestSuite
, TestCase
, and TestStep
operations with Assertion
to specify what we expect back from a successful invoice resource request. We can use the Generate TestSuite option to do this:invoice_v1 Endpoint
and select Generate TestSuite.invoice_v1 TestSuite
.TestSuite
with one generated TestStep
operation for invoice/{id}
.Assertions
to the TestStep
. Say we're expecting a JSON representation of an Invoice document that will look like the following:{"Invoice": { "id": 12345, "companyName": "Test Company", "amount": 100 }}
JsonPath Match Assertions
:Name: IdShouldBe12345 JsonPath: $.Invoice.id expectedValue: 12345 Name: AmountShouldBe100 JsonPath: $.Invoice.amount Expected Value: 100 Name: CompanyNameShouldBeTestCompany JsonPath: $.Invoice.companyName Expected Value: Test Company
Contains Assertions
:Name: ShouldContainText12345 Contains Content: 12345 Name: ShouldContainTextTestCompany Contains Content: Test Company Name: ShouldContainText100 Contains Content: 100
Valid HTTP Status Codes
Assertion
:Name: ShouldReturnHTTPStatus200 HTTP Status Code = 200
Want to also check JSONSchema Compliance?
See the Testing REST response JSON schema compliance recipe of Chapter 4, Web Service Test Scenarios, for how to do it.
wadl2java
script to generate the Java service types and empty the implementation from the WADL.SoapUI's WADL2Java menu option is not what it seems
Unfortunately, in the current version (5.0) of SoapUI, the WADL2Java functionality (http://www.soapui.org/REST-Testing/rest-code-generation.html) is written to use classic wadl2java
(https://wadl.java.net/). This version of wadl2java
only generates the client code from the WADL and not the service code like we need.
<chapter 1 samples>/rest/invoicev1_gen
. Otherwise, you can generate the web service code for invoice_v1.wadl
by running wadl2java.
For example:cd <apache-cxf-3.0.1 home>/ ./bin/wadl2java -d <chapter1 samples>/rest/invoicev1/src/main/java/ -p rest.invoice.v1 -impl -interface <chapter1 samples>/rest/invoicev1/wadl/invoice_v1.wadl
Classpath Issue on MacOSX/Linux
When running wadl2java
with Apache CXF 3.01, if you see this error: Could not find or load main class org.apache.cxf.tools.wadlto.WADLToJava
, then manually setting the CLASSPATH
variable with export
CLASSPATH
=apache-cxf-3.0.1/lib/*
fixes the problem.
Aug 18, 2014 8:57:07 PM org.apache.cxf.common.jaxb.JAXBUtils logGeneratedClassNames INFO: Created classes: generated.Invoice, generated.ObjectFactory
–d
parameter and –p
gives the package structure:rest/invoice/v1/InvoiceserviceV1Resource rest/invoice/v1/InvoiceserviceV1ResourceImpl rest/invoice/v1/Invoice rest/invoice/v1/ObjectFactory rest/invoice/v1/Service
-cp
parameter):cd <chapter1 samples>/rest/invoicev1/src/main/java/rest/invoice/v1/ javac -cp "<apache-cxf-3.0.1 home>/lib/*" -d <chapter1 samples>/rest/invoicev1/target/classes/ *.java
cd <chapter1 samples>/rest/invoicev1/target/classes/ java -cp "<apache-cxf-3.0.1 home>/lib/*:." rest.invoice.v1.Server … INFO logging… … Server ready…
http://localhost:9000/invoiceservice/v1?_wadl
, and you should see a WADL that indicates that the server is running.TestCase
that we created in step 2:TestCase
and edit the TestStep
created in step 2.TestSteps's
request, for example, 12345.TestCase
should result in all the TestStep's
Assertions
failing, and a response with HTTP status 204 no content under the Raw tab. This is expected since we have no implementation yet.InvoiceserviceV1ResourceImpl.java
with the following code:package rest.invoice.v1; public class InvoiceserviceV1ResourceImpl implements InvoiceserviceV1Resource { public Invoice getInvoiceid(String id) { ObjectFactory objectFactory = new ObjectFactory(); Invoice invoice = objectFactory.createInvoice(); if (id != null && id.equals("12345")) { invoice.setId("12345"); invoice.setCompanyName("Test Company"); invoice.setAmount(100.0d); } return invoice; } }
Skip the dev?
A completed version of the code can be found at <chapter1 samples>/rest/invoicev1_impl
.
@XmlRootElement(name = "Invoice")
; otherwise, marshaling from the JavaBean to the response JSON doesn't work:@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "invoice", propOrder = {
"id",
"companyName",
"amount"
})
@XmlRootElement(name = "Invoice")
public class Invoice {
…
Invoice.java
:import javax.xml.bind.annotation.XmlRootElement;
package-info.java
class; otherwise, there will be a namespace prefix on the JSON response.TestCase
should pass!Let's take a look at the main solution points:
Invoice.java
: This is a JavaBean representation of the invoice XML content. This class has binding annotations to allow the Apache CXF JAX-RS implementation to marshal invoice objects to XML content and unmarshal XML content to invoice objects:@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "invoice", propOrder = { "id", "companyName", "amount" }) @XmlRootElement(name = "Invoice")
To understand more about these binding annotations the technology to look at is Java Architecture for XML Binding (JAXB)—see https://jaxb.java.net/tutorial/.
ObjectFactory.java
: This class can optionally be used to create instances of the Invoice.java
class by calling the createInvoice()
factory method. There is also a factory method JAXBElement<Invoice> createInvoice(Invoice value)
to create JAXB invoice XML bindings. These factory methods can be useful to separate object creation code from your service methods when dealing with more complicated schema examples, but they are not especially useful in our case.InvoiceserviceV1Resource.java
:This is a JAX-RS annotated Java interface to represent the RESTful invoice service and its resource. In this example, we have the following code:@Path("/invoiceservice/v1/") public interface InvoiceserviceV1Resource { @GET @Produces("application/json") @Path("/invoice/{id}") Invoice getInvoiceid(@PathParam("id") String id); }
InvoiceserviceV1ResourceImpl
will invoke the getInvoiceid(…)
method passing in the {id} path parameter as the String id
variable if there is a HTTP GET request to the resource /invoiceservice/v1/invoice/{id}
. Other annotated service methods to support POST, PUT and DELETE requests could also be added here and in the implementation. See <chapter 1 samples>/rest/invoice_crud/src/main/java/rest/invoice/crud/v1/InvoiceServiceCRUDV1Resource.java
for an example like this.InvoiceserviceV1ResourceImpl.java
: This is the implementation of the preceding interface to provide the Java code to run when a matching request is made. We added code to the Invoice getInvoiceId(String id)
of this class so that if the invoice (id
) is 12345, then we a create a new Invoice
object using the ObjectFactory
, populate it with the expected values, and return it in the response. In the background, Apache CXF is able to marshal this into JSON content before dispatching the response back to SoapUI. Unlike the JAX-WS example in the first recipe, there was no holder object, so we were responsible for creating the Invoice
object ourselves.Service.java
: This is a server class that publishes our stub service's implementation. Like in the first recipe's JAX-WS server code, the endpoint and service timeout can be set here.Apart from using WADLs to create SoapUI projects for RESTful web services, there are also SoapUI plugins to use more modern alternatives such as RAML(http://raml.org/) and Swagger (http://swagger.io/) definitions as well—see Chapter 10, Using Plugins for more information.
RESTful web services will often be developed code-first and may not present a WADL or a structured definition to generate your SoapUI project and tests from. In these cases, you? can easily build your REST project by manually entering the service's URI, resources, methods, and parameters using their respective menu options, see http://www.soapui.org/getting-started/rest-testing.html. Or if you're a pro version user, you can use SoapUI to generate your project and tests by recording your requests to the service's API (see the next recipe). If you're an open source user, then you can also generate tests in a similar way by using the HTTP Monitor (See http://www.soapui.org/HTTP-Recording/concept.html).
Change the font size
Change margin width
Change background colour