Spring Web Services: Client Integration Testing with MockWebServiceServer

Spring Web Services provides a great way to perform web service client integration tests. However, the example in the documentation requires the client class to extend org.springframework.ws.client.core.support.WebServiceGatewaySupport, which is rather ugly. Instead, I prefer to have WebServiceTemplate injected into my client class.

So, I made a slight tweak to my integration test to work this work…

Production Code To Be Tested

First, we need the following Spring Web Services dependency:

<dependency>
    <groupid>org.springframework.ws</groupid>
    <artifactid>spring-ws-core</artifactid>
    <version>2.1.4.RELEASE</version>
</dependency>

Let’s assume the client class looks like this:-

@Service
public class WSClientServiceImpl implements WSClientService {

    private WebServiceTemplate webServiceTemplate;

    @Autowired
    public WSClientServiceImpl(WebServiceTemplate webServiceTemplate) {
        this.webServiceTemplate = webServiceTemplate;
    }

    public String login(String username, String password) {
        Login login = new ObjectFactory().createLogin();
        login.setUsername(username);
        login.setPassword(password);

        SoapActionCallback callback = new SoapActionCallback("http://webservice/login");
        Object object = webServiceTemplate.marshalSendAndReceive(login, callback);

        JAXBElement<string> jaxbElement = (JAXBElement<string>) object;
        String sessionId = jaxbElement.getValue();

        return sessionId;
    }
}

The WebServiceTemplate bean is configured like this:-

<beans ...="">
    <context:component-scan base-package="com.choonchernlim.epicapp">


    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="contextPaths">
            <list>
                <value>...</value>
            </list>
        </property>
    </bean>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <property name="marshaller" ref="marshaller">
        <property name="unmarshaller" ref="marshaller">
        <property name="defaultUri" value="http://webservice">
    </property></property></property></bean>
</context:component-scan></beans>

Constructing Integration Test

Make sure we have the Spring Web Services Test framework dependency:-

<dependency>
    <groupid>org.springframework.ws</groupid>
    <artifactid>spring-ws-test</artifactid>
    <version>2.1.4.RELEASE</version>
    <scope>test</scope>
</dependency>

The integration test looks similar to the example in the Spring documentation, however, I injected WebServiceTemplate instead:-

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:applicationContext.xml"})
public class WSClientServiceImplTest {

    @Autowired
    private WSClientService wsClientService;

    @Autowired
    private WebServiceTemplate webServiceTemplate;

    private MockWebServiceServer mockServer;

    @Before
    public void createServer() throws Exception {
        mockServer = MockWebServiceServer.createServer(webServiceTemplate);
    }

    @Test
    public void testLogin() {
        Source requestPayload = new StringSource(
                "<ns2:login xmlns:ns2="http://webservice/login">" +
                "  <ns2:username>user</ns2:username>" +
                "  <ns2:password>password</ns2:password>" +
                "</ns2:login>"
        );

        Source responsePayload = new StringSource(
                "<sessionid xmlns="http://webservice/login">12345</sessionid>"
        );

        mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload));

        String sessionId = wsClientService.login("user", "password");

        assertThat(sessionId, is("12345"));

        mockServer.verify();
    }
}

The message with Action ” cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher

PROBLEM

You get this error message when invoking a web service:-

org.springframework.ws.soap.client.SoapFaultClientException: The message with Action ” cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

SOLUTION

You forgot to specify the SOAP action before invoking the web service.

Open your WSDL file and search for the operation you are trying to invoke. You should see something like this:-

<!--?xml version="1.0" encoding="UTF-8"?-->
<wsdl:definitions ...="">
	...
    <wsdl:binding ...="">
		...
        <wsdl:operation name="OhMyGawd">
            <soap:operation soapaction="http://oh.my.gawd">
			...
        </soap:operation></wsdl:operation>
    </wsdl:binding>
	...
</wsdl:definitions>

Take note of the soapAction value, in this example, it is http://oh.my.gawd.

If you are using Spring Web Services, add the following lines:-

@Autowired
private WebServiceTemplate webServiceTemplate;

public void run() {
	ObjectFactory objectFactory = new ObjectFactory();

	// Create the request payload
	OhMyGawd ohMyGawd = objectFactory.createOhMyGawd();
	ohMyGawd.setValue(...);

    OhMyGawdResponse response = (OhMyGawdResponse) webServiceTemplate.marshalSendAndReceive(
            ohMyGawd,
			new SoapActionCallback("http://oh.my.gawd")
	);

	...
}

Cannot process the message because the content type ‘application/soap+xml; charset=utf-8’ was not the expected type ‘text/xml; charset=utf-8’

PROBLEM

You are getting this exception when invoking a web service:-

org.springframework.ws.client.WebServiceTransportException: Cannot process the message because the content type ‘application/soap+xml; charset=utf-8’ was not the expected type ‘text/xml; charset=utf-8’. [415]

SOLUTION

You are using the wrong SOAP version. SOAP v1.1 uses text/xml while SOAP v1.2 uses application/soap+xml.

If you are using Spring Web Services, add the following lines:-

<!--?xml version="1.0" encoding="UTF-8"?-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:oxm="http://www.springframework.org/schema/oxm" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd">

	<!-- Configure the SOAP version to 1.1 -->
    <bean id="soapMessageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
        <property name="soapVersion">
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_11">
        </util:constant></property>
    </bean>

    <oxm:jaxb2-marshaller id="marshaller" contextpath="myproject.wsdl.ws">

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="soapMessageFactory">
        <property name="marshaller" ref="marshaller">
        <property name="unmarshaller" ref="marshaller">
        <property name="defaultUri" value="http://my.web.service?wsdl">
    </property></property></property></constructor-arg></bean>
</oxm:jaxb2-marshaller></beans>