Skip to content

Commit 458bf1c

Browse files
committed
SWS-651 - Add documentation for testing framework
1 parent 29c6915 commit 458bf1c

File tree

1 file changed

+292
-6
lines changed

1 file changed

+292
-6
lines changed

src/docbkx/client.xml

Lines changed: 292 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
</beans>]]></programlisting>
7676
</para>
7777
<para>
78-
The folowing example shows how override the default configuration, and to use Commons Http to
78+
The following example shows how override the default configuration, and to use Commons Http to
7979
authenticate using HTTP authentication:
8080
<programlisting><![CDATA[<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
8181
<constructor-arg ref="messageFactory"/>
@@ -110,7 +110,7 @@
110110
JMS URIs are: <uri>jms:SomeQueue</uri>,
111111
<uri>jms:SomeTopic?priority=3&amp;deliveryMode=NON_PERSISTENT</uri>, and
112112
<uri>jms:RequestQueue?replyToName=ResponseName</uri>.
113-
For more information on this URI syntax, refer to the class level Javadocs of the
113+
For more information on this URI syntax, refer to the class level Javadoc of the
114114
<classname>JmsMessageSender</classname>.
115115
</para>
116116
<para>
@@ -119,8 +119,8 @@
119119
this can be overriden to use <interfacename>TextMessages</interfacename> by using the
120120
<literal>messageType</literal> parameter on the JMS URI. For example:
121121
<uri>jms:Queue?messageType=TEXT_MESSAGE</uri>.
122-
Note that <interfacename>BytesMessages</interfacename> are the prefered type, because
123-
<interfacename>TextMessages</interfacename> do not support attachments and charactering
122+
Note that <interfacename>BytesMessages</interfacename> are the preferred type, because
123+
<interfacename>TextMessages</interfacename> do not support attachments and character
124124
encodings reliably.
125125
</para>
126126
<para>
@@ -302,8 +302,8 @@ public class WebServiceClient {
302302
in the Java code.
303303
</para>
304304
<para>
305-
Please note that the <classname>WebServiceTemplate</classname> class is threadsafe once
306-
configured (assuming that all of it's dependencies are threadsafe too, which is the case for
305+
Please note that the <classname>WebServiceTemplate</classname> class is thread-safe once
306+
configured (assuming that all of it's dependencies are thread-safe too, which is the case for
307307
all of the dependencies that ship with Spring-WS), and so multiple objects can use the same
308308
shared <classname>WebServiceTemplate</classname> instance if so desired.
309309
The <classname>WebServiceTemplate</classname> exposes a zero argument constructor and
@@ -415,4 +415,290 @@ public void marshalWithSoapActionHeader(final Source s) {
415415
}]]></programlisting>
416416
</section>
417417
</section>
418+
<section>
419+
<title>Client-side testing</title>
420+
<para>
421+
When it comes to testing your Web service clients (i.e. classes that uses the
422+
<classname>WebServiceTemplate</classname> to access a Web service), there are two possible
423+
approaches:
424+
</para>
425+
<itemizedlist>
426+
<listitem>
427+
<para>
428+
Write <emphasis>Unit Tests</emphasis>, which simply mock away the
429+
<classname>WebServiceTemplate</classname> class,
430+
<interfacename>WebServiceOperations</interfacename> interface, or the complete client class.
431+
</para>
432+
<para>
433+
The advantage of this approach is that it's quite easy to accomplish; the disadvantage is that
434+
you are not really testing the exact content of the XML messages that are sent over the wire,
435+
especially when mocking out the entire client class.
436+
</para>
437+
</listitem>
438+
<listitem>
439+
<para>
440+
Write <emphasis>Integrations Tests</emphasis>, which do test the contents of the message.
441+
</para>
442+
</listitem>
443+
</itemizedlist>
444+
<para>
445+
The first approach can easily be accomplished with mocking frameworks such as EasyMock, JMock, etc.
446+
The next section will focus on writing integration tests, using the test features introduced in Spring
447+
Web Services 2.0.
448+
</para>
449+
<section>
450+
<title>Writing integration tests</title>
451+
<para>
452+
Spring Web Services 2.0 introduced support for creating Web service client tests.
453+
In this context, a client is a class that uses the <classname>WebServiceTemplate</classname>
454+
to access a Web service.
455+
</para>
456+
<para>
457+
The integration test support lives in the <package>org.springframework.ws.test.client</package> package.
458+
The core class in that package is the <classname>MockWebServiceServer</classname>.
459+
The underlying idea is that the web service template connects to this mock server, sends it request
460+
message, which the mock server then verifies against the registered expectations.
461+
If the expectations are met, the mock server then prepares a response message, which is send back to the
462+
template.
463+
</para>
464+
<para>
465+
The typical usage of the <classname>MockWebServiceServer</classname> is:
466+
<itemizedlist>
467+
<listitem>
468+
<para>
469+
Create a <classname>MockWebServiceServer</classname> instance by calling
470+
<methodname>MockWebServiceServer.createServer(WebServiceTemplate)</methodname>,
471+
<methodname>MockWebServiceServer.createServer(WebServiceGatewaySupport)</methodname>, or
472+
<methodname>MockWebServiceServer.createServer(ApplicationContext)</methodname>.
473+
</para>
474+
</listitem>
475+
<listitem>
476+
<para>
477+
Set up request expectations by calling <methodname>expect(RequestMatcher)</methodname>,
478+
possibly by using the default <interfacename>RequestMatcher</interfacename> implementations
479+
provided in <classname>RequestMatchers</classname> (which can be statically imported).
480+
Multiple expectations can be set up by chaining
481+
<methodname>andExpect(RequestMatcher)</methodname> calls.
482+
</para>
483+
</listitem>
484+
<listitem>
485+
<para>
486+
Create an appropriate response message by calling
487+
<methodname>andRespond(ResponseCreator)</methodname>, possibly by using the default
488+
<interfacename>ResponseCreator</interfacename> implementations provided in
489+
<classname>ResponseCreators</classname> (which can be statically imported).
490+
</para>
491+
</listitem>
492+
<listitem>
493+
<para>
494+
Use the <classname>WebServiceTemplate</classname> as normal, either directly of through
495+
client code.
496+
</para>
497+
</listitem>
498+
<listitem>
499+
<para>
500+
Call <methodname>MockWebServiceServer.verify()</methodname> to make sure that all
501+
expectations have been met.
502+
</para>
503+
</listitem>
504+
</itemizedlist>
505+
</para>
506+
<note>
507+
<para>
508+
Note that the <classname>MockWebServiceServer</classname> (and related classes) offers a
509+
'fluent' API, so you can typically use the Code Completion features (i.e. ctrl-space) in your IDE
510+
to guide you through the process of setting up the mock server.
511+
</para>
512+
</note>
513+
<para>
514+
Consider, for example, this Web service client class:
515+
</para>
516+
<programlistingco>
517+
<areaspec>
518+
<area id="client.test.client.gateway" coords="3"/>
519+
<area id="client.test.client.request" coords="6"/>
520+
<area id="client.test.client.response" coords="10"/>
521+
</areaspec>
522+
<programlisting><![CDATA[import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
523+
524+
public class CustomerClient extends WebServiceGatewaySupport {
525+
526+
public int getCustomerCount() {
527+
CustomerCountRequest request = new CustomerCountRequest();
528+
request.setCustomerName("John Doe");
529+
530+
CustomerCountResponse response =
531+
(CustomerCountResponse) getWebServiceTemplate().marshalSendAndReceive(request);
532+
533+
return response.getCustomerCount();
534+
}
535+
536+
}]]></programlisting>
537+
<calloutlist>
538+
<callout arearefs="client.test.client.gateway">
539+
<para>
540+
The <classname>CustomerClient</classname> extends
541+
<classname>WebServiceGatewaySupport</classname>, which provides it with a
542+
<property>webServiceTemplate</property> property.
543+
</para>
544+
</callout>
545+
<callout arearefs="client.test.client.request">
546+
<para>
547+
<classname>CustomerCountRequest</classname> is an object supported by a marshaller.
548+
For instance, it could have a <interfacename>@XmlRootElement</interfacename> annotation
549+
to be supported by JAXB2.
550+
</para>
551+
</callout>
552+
<callout arearefs="client.test.client.response">
553+
<para>
554+
The <classname>CustomerClient</classname> uses the <classname>WebServiceTemplate</classname>
555+
offered by <classname>WebServiceGatewaySupport</classname> to marshal the request object
556+
into a SOAP message, and sends that to the web service.
557+
The response object is unmarshalled into a <classname>CustomerCountResponse</classname>.
558+
</para>
559+
</callout>
560+
</calloutlist>
561+
</programlistingco>
562+
<para>
563+
A typical test for <classname>CustomerClient</classname> would look like this:
564+
</para>
565+
<programlistingco>
566+
<areaspec>
567+
<areaset id="client.test.test.imports" coords="">
568+
<area id="client.test.test.imports.server" coords="13"/>
569+
<area id="client.test.test.imports.requestmatchers" coords="14"/>
570+
<area id="client.test.test.imports.resonsecreators" coords="15"/>
571+
</areaset>
572+
<areaset id="client.test.test.spring" coords="">
573+
<area id="client.test.test.spring.runwith" coords="17"/>
574+
<area id="client.test.test.spring.configuration" coords="18"/>
575+
</areaset>
576+
<area id="client.test.test.client" coords="22"/>
577+
<area id="client.test.test.mockserver" coords="24"/>
578+
<area id="client.test.test.expectAndRespond" coords="42"/>
579+
<areaset id="client.test.test.client.invoke" coords="">
580+
<area id="client.test.test.client.invoke.actual" coords="44"/>
581+
<area id="client.test.test.client.invoke.assert" coords="45"/>
582+
</areaset>
583+
<area id="client.test.test.client.verify" coords="47"/>
584+
</areaspec>
585+
<programlisting><![CDATA[import javax.xml.transform.Source;
586+
import org.springframework.beans.factory.annotation.Autowired;
587+
import org.springframework.test.context.ContextConfiguration;
588+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
589+
import org.springframework.xml.transform.StringSource;
590+
591+
import org.junit.Before;
592+
import org.junit.Test;
593+
import org.junit.runner.RunWith;
594+
595+
import static org.junit.Assert.assertEquals;
596+
597+
import org.springframework.ws.test.client.MockWebServiceServer;
598+
import static org.springframework.ws.test.client.RequestMatchers.payload;
599+
import static org.springframework.ws.test.client.ResponseCreators.withPayload;
600+
601+
@RunWith(SpringJUnit4ClassRunner.class)
602+
@ContextConfiguration("integration-test.xml")
603+
public class CustomerClientIntegrationTest {
604+
605+
@Autowired
606+
private CustomerClient client;
607+
608+
private MockWebServiceServer mockServer;
609+
610+
@Before
611+
public void createServer() throws Exception {
612+
mockServer = MockWebServiceServer.createServer(client);
613+
}
614+
615+
@Test
616+
public void basic() throws Exception {
617+
Source requestPayload = new StringSource(
618+
"<customerCountRequest xmlns='http://springframework.org/spring-ws'>" +
619+
"<customerName>John Doe</customerName>" +
620+
"</customerCountRequest>");
621+
Source responsePayload = new StringSource(
622+
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
623+
"<customerCount>10</customerCount>" +
624+
"</customerCountResponse>");
625+
626+
mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload));
627+
628+
int result = client.getCustomerCount();
629+
assertEquals(10, result);
630+
631+
mockServer.verify();
632+
}
633+
634+
}]]></programlisting>
635+
<calloutlist>
636+
<callout arearefs="client.test.test.imports">
637+
<para>
638+
The <classname>CustomerClientIntegrationTest</classname> imports the
639+
<classname>MockWebServiceServer</classname>, and statically imports
640+
<classname>RequestMatchers</classname> and <classname>ResponseCreators</classname>.
641+
</para>
642+
</callout>
643+
<callout arearefs="client.test.test.spring">
644+
<para>
645+
This tests uses the standard testing facilities provided in the Spring Framework.
646+
This is not required, but is generally the easiest way to set up the test.
647+
</para>
648+
</callout>
649+
<callout arearefs="client.test.test.client">
650+
<para>
651+
The <classname>CustomerClient</classname> is configured in
652+
<filename>integration-test.xml</filename>, and wired into this test using
653+
<interfacename>@Autowired</interfacename>.
654+
</para>
655+
</callout>
656+
<callout arearefs="client.test.test.mockserver">
657+
<para>
658+
In a <interfacename>@Before</interfacename> method, we create a
659+
<classname>MockWebServiceServer</classname> by using the
660+
<methodname>createServer</methodname> factory method.
661+
</para>
662+
</callout>
663+
<callout arearefs="client.test.test.expectAndRespond">
664+
<para>
665+
We define expectations by calling <methodname>expect()</methodname> with a
666+
<methodname>payload()</methodname> <interfacename>RequestMatcher</interfacename> provided
667+
by the statically imported <classname>RequestMatchers</classname>.
668+
We also set up a response by calling <methodname>andRespond()</methodname> with a
669+
<methodname>withPayload()</methodname> <interfacename>ResponseCreator</interfacename>
670+
provided by the statically imported <classname>ResponseCreators</classname>.
671+
</para>
672+
<para>
673+
This part of the test might look a bit confusing, but the Code Completion features of your
674+
IDE are of great help.
675+
After typing <methodname>expect(</methodname>, simply type ctrl-space, and your IDE will
676+
provide you with a list of possible request matching strategies, provided you
677+
statically imported <classname>RequestMatchers</classname>.
678+
The same applies to <methodname>andRespond(</methodname>, provided you statically imported
679+
<classname>ResponseCreators</classname>.
680+
</para>
681+
</callout>
682+
<callout arearefs="client.test.test.client.invoke">
683+
<para>
684+
We call <methodname>getCustomerCount()</methodname> on the
685+
<classname>CustomerClient</classname>, thus using the
686+
<classname>WebServiceTemplate</classname>.
687+
The template has been set up for 'testing mode' by now, so no real
688+
(HTTP) connection is made by this method call.
689+
We also make some JUnit assertions based on the result of the method call.
690+
</para>
691+
</callout>
692+
<callout arearefs="client.test.test.client.verify">
693+
<para>
694+
We call <methodname>verify()</methodname> on the
695+
<classname>MockWebServiceServer</classname>, thus verifying that the expected
696+
message was actually received.
697+
</para>
698+
</callout>
699+
</calloutlist>
700+
</programlistingco>
701+
702+
</section>
703+
</section>
418704
</chapter>

0 commit comments

Comments
 (0)