Developing a SOAP web service test-first
SoapUI is often used to retrofit tests around web services that are already at least partially developed. To follow a test-first or test-driven development (TDD) approach requires that we first set up failing tests and then provide a service implementation in order to pass them. In this recipe, we'll see how SoapUI can be used to facilitate test-first development for the invoice web service generated in the previous recipe.
Getting ready
We'll need the WSDL from the previous recipe to set up our SoapUI project (<chapter1 samples>/soap/invoicev1/wsdl/invoice_v1.wsdl
).
The Java code for the completed web service implementation can be found at <chapter1 samples>/soap/invoicev1_impl
.
The project can be found at <chapter1 samples>/invoice-soap-v1-soapui-project.xml
.
Tip
Eclipse setup
Optionally, it is very easy to set up an Eclipse project to make light work of the test, edit, compile, and run cycle. First, import the sample code and then run the service as a standard Java application.
How to do it...
Firstly, we'll set up a couple of simple failing tests to assert what we expect back from the getInvoice
operation and then provide basic implementation to pass them. Next, we'll update the invoice WSDL definition to provide an additional createInvoice
operation, write new failing tests, and finally provide basic code to pass those. Perform the following steps:
- To create the SoapUI project and generate the initial
PortBinding
,Test Suite
,TestCase
, andTest Request TestStep
, right-click on yourWorkspace
and select New SOAP Project. In the window, enter/select the following and click on OK:- Project Name:
InvoiceService
- Initial WSDL:
chapter1 samples>/soap/invoicev1/wsdl/invoice_v1.wsdl
- Leave Create Requests ticked and also tick Create TestSuite
- Project Name:
- In the Generate TestSuite window, select the following options and click on OK:
- Leave Style as One TestCase for Each Operation
- Change Request Content to Use existing Requests in Interface
- Accept the suggested
TestSuite
name asInvoicePortBinding TestSuite
in the pop up and click on OK. All expected SoapUI test artifacts should now be generated in your project. - Now, we can write a simple failing test to assert what we expect a successful
getInvoice
request to return. Under the firstTestStep
option, double-click ongetInvoice
and you should see the SOAP request:<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:inv="http://soapui.cookbook.samples/schema/invoice"> <soapenv:Header/> <soapenv:Body> <inv:getInvoice> <inv:invoiceNo>?</inv:invoiceNo> </inv:getInvoice> </soapenv:Body> </soapenv:Envelope>
- Change the
invoiceNo
(?
) value to something more memorable, for example,12345
. - Now, start the stub invoice service generated in the previous recipe and submit the request by clicking on the green arrow. You should see a stubbed response, like the one shown in the following code:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <InvoiceDocument xmlns="http://soapui.cookbook.samples/schema/invoice"> <invoiceNo>12345</invoiceNo> <company/> <amount>0.0</amount> </InvoiceDocument> </S:Body> </S:Envelope>
- Next, let's create some SoapUI
Assertions
to specify the invoice property values we expect to see:invoiceNo
=12345
company
=Test Company
amount
=100.0
Since we're dealing with SOAP XML, let's add 3 XPath
Assertions
to check these values in the response. SoapUI Pro users will find this easy, thanks to the convenient XPath
builder. Open source users can either be 'hardcore' and write them from scratch or just copy the details provided.
Tip
XPath Help
Even the Pro version's XPath builder is of less use when you cannot directly retrieve a response XML to build from, that is, when there is no service at all! As a workaround, you can get SoapUI to generate a sample response XML by going to Add Step | SOAP Mock Response TestStep
from the TestCase
, and then copy the response XML into a helpful XPath tool to write the XPath expression, for example, http://www.freeformatter.com/xpath-tester.html. Paid-for tools such as XML Spy will also help a lot in these areas. You may also find http://www.w3schools.com/XPath/xpath_syntax.asp helpful.
So let's add 3 XPath Assertions
. Edit the REST Request
TestStep
, under the Assertions tab and right-click on Add Assertion and add a new XPath Assertion
to check the response's invoiceNo=12345
, company=Test Company
, and amount=100.0
:
Response |
Assertion name |
XPath Expression |
Expected Result |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Have a look at the following screenshot for better clarity:
Running the TestCase
should now fail 2 of the assertions. Note that InvoiceNoShouldBe12345
will work, thanks to Apache CXF passing through the request's invoiceNo
to the response (see InvoicePortImpl.java
)! It is still worth asserting the invoiceNo
value, as it is a requirement.
Tip
Server timed out?
If you instead see a connection refused
error, then check whether your server hasn't exited after 5 minutes. It's easy to change this timeout (see the previous recipe).
Now, we can add a very basic service implementation to pass this test. We just need to implement the getInvoice(…)
method in InvoicePortImpl.java
. The simplest implementation option is to just edit InvoicePortTypeImpl.java
and hardcode the expected values:
try { java.lang.String companyValue = "Test Company"; company.value = companyValue; java.lang.Double amountValue = 100.0d; amount.value = amountValue; } catch (java.lang.Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); }
Tip
TDD
Strictly speaking, we should first write a unit test before implementing the method, for example, using JUnit.
Next, recompile this and restart the server:
cd <chapter1 samples>/soap/invoicev1 javac src/main/java/ws/invoice/v1/*.java -d target/classes/
And start it again:
cd <chapter1 samples>/soap/invoicev1/target/classes java ws.invoice.v1.InvoicePortType_InvoicePort_Server
Rerun TestCase
, which should now pass!
How it works...
This recipe builds on all the same JAX-WS web service code explained in the previous recipe. This time, we add a very simple stub implementation to return the minimum necessary to pass the test. For those who haven't seen JAX-WS before, the use of the javax.xml.ws.Holder
wrapper object means that we don't have to explicitly set the invoiceNo,
as it is passed through the request (for more information, see http://tomee.apache.org/examples-trunk/webservice-holder/README.html).
There's more...
As mentioned in the previous recipe, SoapUI mocks (see Chapter 3, Developing and Deploying Dynamic REST and SOAP Mocks) can often provide a convenient and often quicker alternative if all you need is a disposable test version of your web service with basic functionality. Also, if you want your web service stub to be the basis for ongoing development, then you may want to consider using a build framework like Gradle or Maven to manage the build, deploy, and test cycle. Chapter 5, Automation and Scripting, looks at different ways to use build frameworks and scripts to run SoapUI tests (and mocks) after your web service is built and deployed. If your stub implementations become more complicated, you may also want unit tests.
The SOAP web service stub journey continues in the next recipe where we use SoapUI to help us update the project, tests, and services to add a createInvoice
operation.
See also
- For more information on Gradle, go to https://www.gradle.org/
- For more information on Maven, go to http://maven.apache.org/
- For more information on JUnit, go to http://junit.org/