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"/>
110110 JMS URIs are: <uri >jms:SomeQueue</uri >,
111111 <uri >jms:SomeTopic?priority=3& 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 >
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