In this chapter, we will cover:
Integration testing using Spring-JUnit support
Server-side integration testing using MockWebServiceClient
Client-side integration testing using MockWebServiceServer
Monitoring TCP messages of a Web-Service using TCPMon
Monitoring and load/functional testing a Web-Service using soapUI
New software development strategies require comprehensive testing in order to achieve the quality in the software development process. Test-driven design (TDD) is an evolutionary approach to the development process, which combines the test-first development process and re-factoring. In the test-first development process, you write a test before writing the complete production code to simplify the test. This testing includes unit testing as well as integration testing.
Spring provides support for integration testing features using the spring-test package. These features include dependency injection and loading the application context within the test environment.
Writing a unit test that uses mock frameworks (such as EasyMock and JMock to test a Web-Service) is quite easy. However, it is not testing the content of the XML messages, so it is not simulating the real production environment of testing.
Spring Web-Services 2.0 provides features to create server-side integration tests as well as the client-side one. Using these integration test features, it is very simple to test a SOAP service without deploying it on the server when you are testing the server side, and without the need to set up a server when you are testing the client side.
In the first recipe, we will discuss how to use the Spring framework for Integration testing. In the next two recipes, new features for integration testing of Spring-WS 2.0 are detailed. In the last two recipes, using tools, such as soapUI and TCPMon for monitoring and testing Web-Services, are presented.
Spring supports integration testing features using the classes in the org.springframework.test
package. These features provide dependency injection in your test case using either the production's application context or any customized one for testing purposes. This recipe presents how to use JUnit test cases using features, spring-test.jar
, JUnit 4.7, and XMLUnit 1.1.
Please note that to run Integration test, we need to start the server. However, in the next two recipes, we will use new features for integration testing of Spring-WS 2.0 that do not require starting up the server.
In this recipe, the project's name is LiveRestaurant_R-3.1
(for server-side Web-Service) and has the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
The following are the Maven dependencies for LiveRestaurant_R-3.1-Client
(for the client-side Web-Service):
spring-ws-core-2.0.1.RELEASE.jar
spring-test-3.0.5.RELEASE.jar
log4j-1.2.9.jar
junit-4.7.jar
xmlunit-1.1.jar
This recipe uses the project used in the recipe Setting up an endpoint by annotating the payload-root discussed in Chapter 1, Building SOAP Web-Services, as the server-side project. Here is the setup for the client side:
Create a test class that calls the Web-Service server using WebServiceTemplate
in src/test
.
Configure WebServiceTemplate
in applicationContext.xml
.
From the folder Liverestaurant_R-3.1
, run the following command:
mvn clean package tomcat:run
Open a new command window to Liverestaurant_R-3.1-Client
and run the following command:
mvn clean package.
The following is the client-side output:
.................
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.packtpub.liverestaurant.client.OrderServiceClientTest
............................
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.633 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
The server-side projects set up a Web-Service server and the client-side project runs an integration test and sends predefined request messages to the server and gets the response message from the server. Then compare the server response with the expected response. Setting up a Web-Service and a client of the Web-Service have already been detailed in the first two chapters. Here, only the testing framework is detailed.
In OrderServiceClientTest.java
, the method setUpBefore()
will be called first to initialize data (since it is annotated by @before)
and test methods that are annotated by @Test
(testCancelOrderRequest
or testPalceOrderRequest)
to follow, and finally, the method setUpAfter()
will be called to free up the resources (since it is annotated by @after)
.
When you run mvn clean package
, Maven builds and runs any test class inside the src/test/java
folder. So in OrderServiceClientTest.java
, first the test application context will be loaded. In the application context, only the configuration of WebServiceTemplate
is required:
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" /> <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> <constructor-arg ref="messageFactory" /> <property name="defaultUri" value="http://localhost:8080/LiveRestaurant/spring-ws/OrderService" /> </bean>
In OrderServiceClientTest.java
, to include the Spring dependency injection, and to set up and run the test, code is annotated with some information. The JUnit @RunWith
annotation tells JUnit to use the Spring TestRunner
. The @ContextConfiguration
annotation from Spring tells to load which application context and use this context to inject applicationContext
and webServiceTemplate
, which are annotated with @Autowired:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/applicationContext.xml") public class OrderServiceClientTest { @Autowired private WebServiceTemplate webServiceTemplate; ........
@Before
from JUnit tells to run the marked method (setUpBefore
) before running the test case. JUnit @After
causes the marked method to be called after the test case is executed. @Test
from JUnit converts the marked methods (testCancelOrderRequest
and testPlaceOrderRequest)
into JUnit test methods:
@After public void setUpAfter() { applicationContext.close(); } @Test public final void testPlaceOrderRequest() throws Exception { Result result = invokeWS(placeOrderRequest); XMLAssert.assertXMLEqual("Invalid content received", getStringFromInputStream(placeOrderResponse), result.toString()); } @Test public final void testCancelOrderRequest() throws Exception { Result result = invokeWS(cancelOrderRequest); XMLAssert.assertXMLEqual("Invalid content received", getStringFromInputStream(cancelOrderResponse), result.toString()); } private Result invokeWS(InputStream is) { StreamSource source = new StreamSource(is); StringResult result = new StringResult(); webServiceTemplate.sendSourceAndReceiveToResult(source, result); return result; } public String getStringFromInputStream (InputStream is) throws IOException { BufferedInputStream bis = new BufferedInputStream(is); ByteArrayOutputStream buf = new ByteArrayOutputStream(); int result = bis.read(); while(result != -1) { byte b = (byte)result; buf.write(b); result = bis.read(); } return buf.toString(); }
Note that for each test method, the @After
and @Before
methods will be executed once. XMLAssert.assertXMLEqual
compares the real result and the expected XML messages.
Writing a unit test that uses mock frameworks, such as EasyMock and JMock, to test a Web-Service is quite easy. However, it does not test the content of the XML messages, so it is not simulating the real production environment of testing (since these mock objects mimic a part of the software, which is not running, this is neither unit testing nor integration testing).
Spring Web-Services 2.0 provides features to create server-side integration tests. Using this feature, it is very simple to test a SOAP service without deploying on the server and without the need to configure a test client in the Spring configuration file.
The main class of server-side integration tests is MockWebServiceClient
from the org.springframework.ws.test.server
package. This class creates a request message, sends the request to the service, and gets the response message. The client compares the response with the expected message.
In this recipe, the project's name is LiveRestaurant_R-3.2
(as the server-side Web-Service that includes a test case that uses MockWebServiceClient)
and has the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
spring-ws-test-2.0.1.RELEASE.jar
spring-test-3.0.5.RELEASE.jar
log4j-1.2.9.jar
junit-4.7.jar
This recipe uses the project from Setting up an endpoint by annotating the payload-root, discussed in Chapter 1, Building SOAP Web-Services, as the server-side project. Here is the setup for the test case:
Include the following data in pom.xml:
<testResources> <testResource> <directory>src/main/webapp</directory> </testResource> </testResources> </build>
Add the test case class in the folder src/test/java.
Run the following command for Liverestaurant_R-3.2:
mvn clean package
The following is the server-side output:
..................
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.packtpub.liverestaurant.service.test.OrderServiceServerSideIntegrationTest
l.........
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.047 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
In the class OrderServiceServerSideIntegrationTest.java
, annotation and unit testing materials are the same as those used in the recipe Integration testing using Spring-JUnit support. The only difference here is that we are not setting up the server. Instead, we load the server application context in the test case class:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/WEB-INF/spring-ws-servlet.xml") public class OrderServiceServerSideIntegrationTest { .......................
The test case class, in the @Before
method, initializes an instance of the client mock object and XML messages:
@Before public void createClient() { wsMockClient = MockWebServiceClient.createClient(applicationContext); placeOrderRequest = new OrderServiceServerSideIntegrationTest().getClass().getResourceAsStream("placeOrderRequest.xml"); cancelOrderRequest = new OrderServiceServerSideIntegrationTest().getClass().getResourceAsStream("cancelOrderRequest.xml"); placeOrderResponse = new OrderServiceServerSideIntegrationTest().getClass().getResourceAsStream("placeOrderResponse.xml"); cancelOrderRsponse = new OrderServiceServerSideIntegrationTest().getClass().getResourceAsStream("cancelOrderResponse.xml"); }
Then, it sends a message and receives the response. It then compares the expected response and the real response:
@After public void setUpAfterClass() { applicationContext.close(); } @Test public final void testPlaceOrderRequest() throws Exception { Source requestPayload = new StreamSource(placeOrderRequest); Source responsePayload = new StreamSource(placeOrderResponse); wsMockClient.sendRequest(withPayload(requestPayload)). andExpect(payload(responsePayload)); } @Test public final void testCancelOrderRequest() throws Exception { Source requestPayload = new StreamSource(cancelOrderRequest); Source responsePayload = new StreamSource(cancelOrderRsponse); wsMockClient.sendRequest(withPayload(requestPayload)). andExpect(payload(responsePayload)); }
In the method createClient(), MockWebServiceClient.createClient(applicationContext)
creates an instance of the client mock object (wsMockClient
). In the test case methods (testCancelOrderRequest, testPlaceOrderRequest)
, using the code wsMockClient.sendRequest(withPayload(requestPayload)).andExpect(payload(responsePayload))
, the mock client sends an XML message and compares the response (from server endpoint) with the expected response (The client mock is aware of server endpoint from application context file and when it sends request to server, invokes the endpoint method and gets the response back).
The recipes Integration testing using Spring-JUnit support and Client-side integration testing using MockWebServiceServer, discussed in this chapter.
The recipe Setting up an endpoint by annotating the payload-root, discussed in Chapter 1,Building SOAP Web-Services.
Writing a client-side unit test that uses mock frameworks to test a client of a Web-Service is quite easy. However, it does not test the content of the XML messages that are sent over the wire, especially when mocking out the entire client class.
Spring Web-Services 2.0 provides features to create client-side integration tests. Using this feature, it is very simple to test the client of a SOAP service without setting up a server.
The main class of client-side integration tests is MockWebServiceServer
from the org.springframework.ws.test.server
package. This class accepts a request message from a client, verifies it against the expected request messages, and then returns the response message back to the client.
Since this project is a client-side test integration using MockWebServiceServer
, it doesn't need any external server-side Web-Service.
In this recipe, the project's name is LiveRestaurant_R-3.3-Client
(as the client-side project that includes a test case that uses MockServiceServer
as the server) and has the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
spring-ws-test-2.0.1.RELEASE.jar
spring-test-3.0.5.RELEASE.jar
log4j-1.2.9.jar
junit-4.7.jar
This recipe uses the client-side project from Creating a Web-Service client on HTTP transport, discussed in Chapter 2, Building Clients for SOAP Web-Services. Here is the setup for the test case:
Create a test case class under src/test
.
Create a class that extends WebServiceGatewaySupport
to send/receive messages.
Run the following command for Liverestaurant_R-3.3-Client:
mvn clean package
The following is the client-side output:
**************************
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.packtpub.liverestaurant.client.test.ClientSideIntegrationTest
........
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.945 sec
Results :
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
The flow in the test case class ClientSideIntegrationTest.java
is as follows:
Create a MockWebServiceServer
using WebServiceGatewaySupport
(OrderServiceClient
that extends WebServiceGatewaySupport)
. You can also create MockWebServiceServer
using WebServiceTemplate
or using ApplicationContext
.
Set up request expectations using RequestMatcher
and return the response using ResponseCreator
.
Make a client call by using the WebServiceTemplate
.
Call the verify
method to make sure all the expectations are met. The application context file is just a configuration of WebServiceTemplate
and OrderServiceClient:
<bean id="client" class=" com.packtpub.liverestaurant.client.test.OrderServiceClient"> <property name="webServiceTemplate" ref="webServiceTemplate"/> </bean> <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> <property name="defaultUri" value="http://www.packtpub.com/liverestaurant/OrderService/schema"/> </bean> </beans>
Inside ClientSideIntegrationTest.java
, the annotation and unit testing materials are the same as those used in the recipe Integration testing using Spring-JUnit support. The method createServer()
creates MockWebServiceServer
using WebServiceGatewaySupport
(OrderServiceClient
extends WebServiceGatewaySupport):
public class OrderServiceClient extends WebServiceGatewaySupport { public Result getStringResult(Source source) { StringResult result = new StringResult(); getWebServiceTemplate().sendSourceAndReceiveToResult(source, result); return result; } }
In the test, the method testExpectedRequestResponse, mockServer.expect
sets the expected request and response (webServiceTemplate
is configured in 'testing mode' in client-integration-test.xml
. When the sendSourceAndReceiveToResult
method is being called, the template calls server virtually without any real HTTP connection). Then client.getStringResult
calls webserviceTemplate
to call the server (MockWebServiceServer)
. Then, mockServer.verify
checks if the returned response matches the expected one:
@Test public void testExpectedRequestResponse() throws Exception { Source requestPayload = new StringSource(getStringFromInputStream(placeOrderRequest)); Source responsePayload = new StringSource(getStringFromInputStream(placeOrderResponse)); mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload)); Result result = client.getStringResult(requestPayload); XMLAssert.assertXMLEqual("Invalid content received", xmlToString(responsePayload), result.toString()); mockServer.verify(); }
In the test method testSchema
, instead of using a hardcoded request/response, the schema of the expected request and response is used. This test can test if the format of the request/response is as expected. This is shown as follows:
. @Test public void testSchema() throws Exception { Resource schema=new FileSystemResource("orderService.xsd"); mockServer.expect(validPayload(schema)); client.getStringResult(new StreamSource(placeOrderRequest)); mockServer.verify(); }
In the test method testSchemaWithWrongRequest
, the schema of the expected request and response is used. However, the client is trying to send invalid request, that is to be failed:
@Test(expected = AssertionError.class) public void testSchemaWithWrongRequest() throws Exception { Resource schema=new FileSystemResource("orderService.xsd"); mockServer.expect(validPayload(schema)); client.getStringResult(new StringSource(getStringFromInputStream(cancelOrderRequestWrong))); mockServer.verify(); }
TCPMon is an Apache project with a Swing UI, which provides features to monitor TCP-based messages transmitted between the client and server. A SOAP message can also be sent to the server using TCPMon.
This recipe presents how to monitor messages passed between a Web-Service client and the server. In addition, it shows how to send a SOAP message using TCPMon. The recipe Integration testing using Spring-JUnit support is used for server-side and client-side projects.
Download and install TCPMon 1.0 from the website http://ws.apache.org/commons/tcpmon/download.cgi.
Monitor the messages between the client and server as follows:
Run it on Windows using tcpmon.bat (tcpmon.sh
for Linux).
Enter the values 8081 and 8080 into the Listen port # and Target port # fields and click on the Add option.
Change applicationContext.xml
in LiveRestaurant_R-3.1-Client
to use the 8081 port for webserviceTemplate:
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" /> <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> <constructor-arg ref="messageFactory" /> <property name="defaultUri" value="http://localhost:8081/LiveRestaurant/spring-ws/OrderService" /> </bean>
Run the server from the project LiveRestaurant_R-3.1
using the following command:
mvn clean package tomcat:run
Run the client from the project LiveRestaurant_R-3.1-Client
using the following command:
mvn clean package
Go to the Port 8081 tab and see request and response messages, as shown in the following screenshot:
Send a SOAP request to the server as follows:
Go to the Sender tab. Enter the SOAP service address and a SOAP request message and click on the Send button to view the response:
Monitoring transmitted messages between a client and a Web-Service server is the most important usage of the TCPMon. In addition, TCPMon can be used as a client to send a message to a Web-Service server. This is an intermediary role that shows the transmitted messages between the client and server. The client has to point to the intermediary instead of the server service.
The second activity (sending a SOAP request to the server) shows the sending of a message using TCPMon to the server, the reception of the response, and shows all of this on TCPMon.
soapUI is an open source testing solution for testing web services. Using a user-friendly GUI, this tool provides a feature to create and execute automated functional and load testing as well as monitor SOAP messages.
This recipe presents how to monitor SOAP messages of the Web-Service and functional and load testing using soapUI. To set up a Web-Service, Recipe 3.1
, Integration testing using Spring-JUnit support, is used.
Get started by carrying out the following steps:
Install and run soapUI 4.0 (http://www.soapui.org/).
Run the following command from the folder LiveRestaurant_R-3.1:
mvn clean package tomcat:run
To run the functional tests and monitor the SOAP messages, carry out the following steps:
Right-click on the Projects node. Select New soapUI Project and enter the WSDL URL and the Project Name.
Right-click on the project's name, OrderService, in the navigator pane. Select Launch HTTP Monitor and enable the option Set as Global Proxy. Click on the OK button:
Expand the OrderService methods (cancelOrder and placeOrder). Double-click cancelOrder. Click on Submit Request to Specific Endpoint URL (The green icon on the top-left corner of the Request1 screen). The following is the output of this action:
Right-click OrderServiceSoap11 | Generate Test Suite | OK. Enter OrderServiceSoap11 TestSuite.
Double-click on OrderServiceSoap11 TestSuite on the navigator pane. Click Run the selected TestCases.
Run a load test as follows:
Functional testing and monitoring SOAP messages: soapUI provides three levels of functional testing: test suites, test cases, and test steps.
Test cases are the unit tests that are generated from the WSDL file and test suites are a collection of these unit tests. Test steps control the flow of execution and validate the functionality of the service that is to be tested. For example, a test case in the test suite for the cancelOrder mentioned previously may test the database first. If there is such an order available, it cancels the order.
Load testing: soapUI provides a feature to run multiple threads (as many as your machine's hardware limits you to) on your test cases. When you run a load test, the underlying test case will be cloned internally for each thread. Delay settings let each thread wait before starting and let the Web-Service rest for each thread.