SWS-651 - Finished server-side docs

This commit is contained in:
Arjen Poutsma
2010-11-09 15:39:29 +00:00
parent 01bcd2661a
commit 5aca7c3087
2 changed files with 374 additions and 13 deletions

View File

@@ -447,9 +447,9 @@ public void marshalWithSoapActionHeader(final Source s) {
Web Services 2.0.
</para>
<section>
<title>Writing integration tests</title>
<title>Writing client-side integration tests</title>
<para>
Spring Web Services 2.0 introduced support for creating Web service client tests.
Spring Web Services 2.0 introduced support for creating Web service client integration tests.
In this context, a client is a class that uses the <classname>WebServiceTemplate</classname>
to access a Web service.
</para>
@@ -463,7 +463,7 @@ public void marshalWithSoapActionHeader(final Source s) {
</para>
<para>
The typical usage of the <classname>MockWebServiceServer</classname> is:
<itemizedlist>
<orderedlist>
<listitem>
<para>
Create a <classname>MockWebServiceServer</classname> instance by calling
@@ -501,7 +501,7 @@ public void marshalWithSoapActionHeader(final Source s) {
expectations have been met.
</para>
</listitem>
</itemizedlist>
</orderedlist>
</para>
<note>
<para>
@@ -554,7 +554,7 @@ public class CustomerClient extends WebServiceGatewaySupport {
<callout arearefs="client.test.client.request">
<para>
<classname>CustomerCountRequest</classname> is an object supported by a marshaller.
For instance, it could have a <interfacename>@XmlRootElement</interfacename> annotation
For instance, it can have a <interfacename>@XmlRootElement</interfacename> annotation
to be supported by JAXB2.
</para>
</callout>
@@ -575,8 +575,8 @@ public class CustomerClient extends WebServiceGatewaySupport {
<areaspec>
<areaset id="client.test.test.imports" coords="">
<area id="client.test.test.imports.server" coords="13"/>
<area id="client.test.test.imports.requestmatchers" coords="14"/>
<area id="client.test.test.imports.resonsecreators" coords="15"/>
<area id="client.test.test.imports.requestMatchers" coords="14"/>
<area id="client.test.test.imports.responseCreators" coords="15"/>
</areaset>
<areaset id="client.test.test.spring" coords="">
<area id="client.test.test.spring.runwith" coords="17"/>
@@ -604,8 +604,8 @@ import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import org.springframework.ws.test.client.MockWebServiceServer;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;
import static org.springframework.ws.test.client.RequestMatchers.*;
import static org.springframework.ws.test.client.ResponseCreators.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("integration-test.xml")
@@ -622,7 +622,7 @@ public class CustomerClientIntegrationTest {
}
@Test
public void basic() throws Exception {
public void customerClient() throws Exception {
Source requestPayload = new StringSource(
"<customerCountRequest xmlns='http://springframework.org/spring-ws'>" +
"<customerName>John Doe</customerName>" +
@@ -651,7 +651,7 @@ public class CustomerClientIntegrationTest {
</callout>
<callout arearefs="client.test.test.spring">
<para>
This tests uses the standard testing facilities provided in the Spring Framework.
This test uses the standard testing facilities provided in the Spring Framework.
This is not required, but is generally the easiest way to set up the test.
</para>
</callout>

View File

@@ -149,7 +149,7 @@
<literal>[servlet-name]-servlet.xml</literal></emphasis> in the <literal>WEB-INF</literal> directory
of your web application and create the beans defined there in a Spring container. In the example above,
that means that it looks for '<filename>/WEB-INF/spring-ws-servlet.xml</filename>'. This file will
contain all of the SWS-specific beans such as endpoints, marshallers and suchlike.
contain all of the Spring Web Services beans such as endpoints, marshallers and suchlike.
</para>
<section id="server-automatic-wsdl-exposure">
<title>Automatic WSDL exposure</title>
@@ -583,7 +583,7 @@
</para>
</section>
</section>
<section>
<section id="server-endpoints">
<title>Endpoints</title>
<para>
Endpoints are the central concept in Spring-WS's server-side support. Endpoints provide access to the
@@ -1566,4 +1566,365 @@ public class MyBusinessException extends Exception {
</SOAP-ENV:Envelope>]]></programlisting>
</section>
</section>
<section>
<title>Server-side testing</title>
<para>
When it comes to testing your Web service endpoints, there are two possible approaches:
</para>
<itemizedlist>
<listitem>
<para>
Write <emphasis>Unit Tests</emphasis>, where you provide (mock) arguments for your endpoint to
consume.
</para>
<para>
The advantage of this approach is that it's quite easy to accomplish (especially for classes
annotated with <interfacename>@Endpoint</interfacename>); the disadvantage is that
you are not really testing the exact content of the XML messages that are sent over the wire.
</para>
</listitem>
<listitem>
<para>
Write <emphasis>Integrations Tests</emphasis>, which do test the contents of the message.
</para>
</listitem>
</itemizedlist>
<para>
The first approach can easily be accomplished with mocking frameworks such as EasyMock, JMock, etc.
The next section will focus on writing integration tests, using the test features introduced in Spring
Web Services 2.0.
</para>
<section>
<title>Writing server-side integration tests</title>
<para>
Spring Web Services 2.0 introduced support for creating endpoint integration tests.
In this context, an endpoint is class handles (SOAP) messages (see <xref linkend="server-endpoints"/>).
</para>
<para>
The integration test support lives in the <package>org.springframework.ws.test.server</package> package.
The core class in that package is the <classname>MockWebServiceClient</classname>.
The underlying idea is that this client creates a request message, and then sends it over to the
endpoint(s) that are configured in a standard <classname>MessageDispatcherServlet</classname>
application context (see <xref linkend="message-dispatcher-servlet"/>).
These endpoints will handle the message, and create a response.
The client then receives this response, and verifies it against registered expectations.
</para>
<para>
The typical usage of the <classname>MockWebServiceClient</classname> is:
<orderedlist>
<listitem>
<para>
Create a <classname>MockWebServiceClient</classname> instance by calling
<methodname>MockWebServiceClient.createClient(ApplicationContext)</methodname> or
<methodname>MockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory)</methodname>.
</para>
</listitem>
<listitem>
<para>
Send request messages by calling <methodname>sendRequest(RequestCreator)</methodname>,
possibly by using the default <interfacename>RequestCreator</interfacename> implementations
provided in <classname>RequestCreators</classname> (which can be statically imported).
</para>
</listitem>
<listitem>
<para>
Set up response expectations by calling <methodname>andExpect(ResponseMatcher)</methodname>,
possibly by using the default <interfacename>ResponseMatcher</interfacename> implementations
provided in <classname>ResponseMatchers</classname> (which can be statically imported).
Multiple expectations can be set up by chaining
<methodname>andExpect(ResponseMatcher)</methodname> calls.
</para>
</listitem>
</orderedlist>
</para>
<note>
<para>
Note that the <classname>MockWebServiceClient</classname> (and related classes) offers a
'fluent' API, so you can typically use the Code Completion features (i.e. ctrl-space) in your IDE
to guide you through the process of setting up the mock server.
</para>
</note>
<note>
<para>
Also note that you rely on the standard logging features available in Spring Web Services in your
unit tests.
Sometimes it might be useful to inspect the request or response message to find out why a
particular tests failed.
See <xref linkend="logging"/> for more information.
</para>
</note>
<para>
Consider, for example, this simple Web service endpoint class:
</para>
<programlistingco>
<areaspec>
<area id="server.test.endpoint.annotation" coords="5"/>
<areaset id="server.test.endpoint.method" coords="">
<area id="server.test.endpoint.method.responsePayload" coords="8"/>
<area id="server.test.endpoint.method.method" coords="9"/>
<area id="server.test.endpoint.method.requestPayload" coords="10"/>
</areaset>
</areaspec>
<programlisting><![CDATA[import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
@Endpoint
public class CustomerEndpoint {
@ResponsePayload
public CustomerCountResponse getCustomerCount(
@RequestPayload CustomerCountRequest request) {
CustomerCountResponse response = new CustomerCountResponse();
response.setCustomerCount(10);
return response;
}
}]]></programlisting>
<calloutlist>
<callout arearefs="server.test.endpoint.annotation">
<para>
The <classname>CustomerEndpoint</classname> in annotated with
<interfacename>@Endpoint</interfacename>.
See <xref linkend="server-at-endpoint"/>.
</para>
</callout>
<callout arearefs="server.test.endpoint.method">
<para>
The <methodname>getCustomerCount()</methodname> method takes a
<classname>CustomerCountRequest</classname> as argument, and returns a
<classname>CustomerCountResponse</classname>.
Both of these classes are objects supported by a marshaller.
For instance, they can have a <interfacename>@XmlRootElement</interfacename> annotation
to be supported by JAXB2.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
A typical test for <classname>CustomerEndpoint</classname> would look like this:
</para>
<programlistingco>
<areaspec>
<areaset id="server.test.test.imports" coords="">
<area id="server.test.test.imports.client" coords="12"/>
<area id="server.test.test.imports.requestCreators" coords="13"/>
<area id="client.test.test.imports.responseMatchers" coords="14"/>
</areaset>
<areaset id="server.test.test.spring" coords="">
<area id="server.test.test.spring.runWith" coords="16"/>
<area id="server.test.test.spring.configuration" coords="17"/>
</areaset>
<area id="server.test.test.applicationContext" coords="21"/>
<area id="server.test.test.mockClient" coords="27"/>
<areaset id="server.test.test.sendAndExpect" coords="">
<area id="server.test.test.send" coords="41"/>
<area id="server.test.test.expect" coords="42"/>
</areaset>
</areaspec>
<programlisting><![CDATA[import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.xml.transform.StringSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.ws.test.server.MockWebServiceClient;
import static org.springframework.ws.test.server.RequestCreators.*;
import static org.springframework.ws.test.server.ResponseMatchers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("spring-ws-servlet.xml")
public class CustomerEndpointIntegrationTest {
@Autowired
private ApplicationContext applicationContext;
private MockWebServiceClient mockClient;
@Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext);
}
@Test
public void customerEndpoint() throws Exception {
Source requestPayload = new StringSource(
"<customerCountRequest xmlns='http://springframework.org/spring-ws'>" +
"<customerName>John Doe</customerName>" +
"</customerCountRequest>");
Source responsePayload = new StringSource(
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
"<customerCount>10</customerCount>" +
"</customerCountResponse>");
mockClient.sendRequest(withPayload(requestPayload)).
andExpect(payload(responsePayload));
}
}]]></programlisting>
<calloutlist>
<callout arearefs="server.test.test.imports">
<para>
The <classname>CustomerEndpointIntegrationTest</classname> imports the
<classname>MockWebServiceClient</classname>, and statically imports
<classname>RequestCreators</classname> and <classname>ResponseMatchers</classname>.
</para>
</callout>
<callout arearefs="server.test.test.spring">
<para>
This test uses the standard testing facilities provided in the Spring Framework.
This is not required, but is generally the easiest way to set up the test.
</para>
</callout>
<callout arearefs="server.test.test.applicationContext">
<para>
The application context is a standard Spring-WS application context (see
<xref linkend="message-dispatcher-servlet"/>), read from
<filename>spring-ws-servlet.xml</filename>.
In this case, the application context will contain a bean definition for
<classname>CustomerEndpoint</classname> (or a perhaps a
<literal>&lt;context:component-scan /&gt;</literal> is used).
</para>
</callout>
<callout arearefs="server.test.test.mockClient">
<para>
In a <interfacename>@Before</interfacename> method, we create a
<classname>MockWebServiceClient</classname> by using the
<methodname>createClient</methodname> factory method.
</para>
</callout>
<callout arearefs="server.test.test.sendAndExpect">
<para>
We send a request by calling <methodname>sendRequest()</methodname> with a
<methodname>withPayload()</methodname> <interfacename>RequestCreator</interfacename>
provided by the statically imported <classname>RequestCreators</classname> (see <xref
linkend="server-test-request-creator"/>).
</para>
<para>
We also set up response expectations by calling <methodname>andExpect()</methodname> with a
<methodname>payload()</methodname> <interfacename>ResponseMatcher</interfacename> provided
by the statically imported <classname>ResponseMatchers</classname> (see <xref
linkend="server-test-response-matcher"/>).
</para>
<para>
This part of the test might look a bit confusing, but the Code Completion features of your
IDE are of great help.
After typing <methodname>sendRequest(</methodname>, simply type ctrl-space, and your IDE
will provide you with a list of possible request creating strategies, provided you
statically imported <classname>RequestCreators</classname>.
The same applies to <methodname>andExpect(</methodname>, provided you statically imported
<classname>ResponseMatchers</classname>.
</para>
</callout>
</calloutlist>
</programlistingco>
</section>
<section id="server-test-request-creator">
<title><interfacename>RequestCreator</interfacename> and <classname>RequestCreators</classname></title>
<para>
Initially, the <classname>MockWebServiceClient</classname> will need to create a request message for the
endpoint to consume.
The client uses the <interfacename>RequestCreator</interfacename>
strategy interface for this purpose:
</para>
<programlisting><![CDATA[public interface RequestCreator {
WebServiceMessage createRequest(WebServiceMessageFactory messageFactory)
throws IOException;
}]]></programlisting>
<para>
You can write your own implementations of this interface, creating a request message
by using the message factory, but you certainly do not have to.
The <classname>RequestCreators</classname> class provides a way to create a
<interfacename>RequestCreator</interfacename> based on a given payload in the
<methodname>withPayload()</methodname> method.
You will typically statically import <classname>RequestCreators</classname>.
</para>
</section>
<section id="server-test-response-matcher">
<title><interfacename>ResponseMatcher</interfacename> and <classname>ResponseMatchers</classname></title>
<para>
When the request message has been processed by the endpoint, and a response has been received,
the <classname>MockWebServiceClient</classname> can verify whether this response message meets certain
expectations.
The client uses the <interfacename>ResponseMatcher</interfacename> strategy interface for this purpose:
</para>
<programlisting><![CDATA[public interface ResponseMatcher {
void match(WebServiceMessage request,
WebServiceMessage response)
throws IOException, AssertionError;
}]]></programlisting>
<para>
Once again you can write your own implementations of this interface, throwing
<classname>AssertionError</classname>s when the message does not meet your expectations, but you
certainly do not have to, as the <classname>ResponseMatchers</classname> class provides standard
<interfacename>ResponseMatcher</interfacename> implementations for you to use in your tests.
You will typically statically import this class.
</para>
<para>
The <classname>ResponseMatchers</classname> class provides the following request matchers:
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry><classname>ResponseMatchers</classname> method</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><methodname>payload()</methodname></entry>
<entry>Expects a given response payload.</entry>
</row>
<row>
<entry><methodname>validPayload()</methodname></entry>
<entry>Expects the response payload to validate against given XSD schema(s).</entry>
</row>
<row>
<entry><methodname>xpath()</methodname></entry>
<entry>
Expects a given XPath expression to exist, not exist, or evaluate to a given
value.
</entry>
</row>
<row>
<entry><methodname>soapHeader()</methodname></entry>
<entry>Expects a given SOAP header to exist in the response message.</entry>
</row>
<row>
<entry><methodname>noFault()</methodname></entry>
<entry>Expects that the response message does not contain a SOAP Fault.</entry>
</row>
<row>
<entry>
<methodname>mustUnderstandFault()</methodname>,
<methodname>clientOrSenderFault()</methodname>,
<methodname>serverOrReceiverFault()</methodname>, and
<methodname>versionMismatchFault()</methodname>
</entry>
<entry>Expects the response message to contain a specific SOAP Fault.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
You can set up multiple response expectations by chaining <methodname>andExpect()</methodname> calls,
like so:
<programlisting>mockClient.sendRequest(...).
andExpect(payload(expectedResponsePayload)).
andExpect(validPayload(schemaResource));
</programlisting>
</para>
<para>
For more information on the request matchers provided by <classname>ResponseMatchers</classname>,
refer to the class level Javadoc.
</para>
</section>
</section>
</chapter>