SWS-631 - Create Client-Side testing framework

This commit is contained in:
Arjen Poutsma
2010-11-03 09:22:10 +00:00
parent 3f0e9f7d44
commit 49e75360ca
11 changed files with 400 additions and 24 deletions

View File

@@ -21,7 +21,6 @@ import java.net.URI;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.xml.transform.TransformerObjectSupport;
/**
* Abstract base class for the {@link ResponseCreator} interface.
@@ -32,11 +31,12 @@ import org.springframework.xml.transform.TransformerObjectSupport;
* @author Arjen Poutsma
* @since 2.0
*/
abstract class AbstractResponseCreator<T extends WebServiceMessage> extends TransformerObjectSupport
implements ResponseCreator<T> {
abstract class AbstractResponseCreator implements ResponseCreator {
public final T createResponse(URI uri, T request, WebServiceMessageFactory<? extends T> messageFactory) throws IOException {
T response = messageFactory.createWebServiceMessage();
public final WebServiceMessage createResponse(URI uri,
WebServiceMessage request,
WebServiceMessageFactory messageFactory) throws IOException {
WebServiceMessage response = messageFactory.createWebServiceMessage();
doWithResponse(uri, request, response);
return response;
}
@@ -49,6 +49,7 @@ abstract class AbstractResponseCreator<T extends WebServiceMessage> extends Tran
* @param response the response message
* @throws IOException in case of I/O errors
*/
protected abstract void doWithResponse(URI uri, T request, T response) throws IOException;
protected abstract void doWithResponse(URI uri, WebServiceMessage request, WebServiceMessage response)
throws IOException;
}

View File

@@ -29,7 +29,7 @@ import org.springframework.ws.WebServiceMessageFactory;
* @author Lukas Krecan
* @since 2.0
*/
class ErrorResponseCreator implements ResponseCreator<WebServiceMessage> {
class ErrorResponseCreator implements ResponseCreator {
private final String errorMessage;
@@ -39,7 +39,7 @@ class ErrorResponseCreator implements ResponseCreator<WebServiceMessage> {
public WebServiceMessage createResponse(URI uri,
WebServiceMessage request,
WebServiceMessageFactory<? extends WebServiceMessage> factory) throws IOException {
WebServiceMessageFactory factory) throws IOException {
// Do nothing
return null;
}

View File

@@ -29,7 +29,7 @@ import org.springframework.ws.WebServiceMessageFactory;
* @author Arjen Poutsma
* @since 2.0
*/
class ExceptionResponseCreator implements ResponseCreator<WebServiceMessage> {
class ExceptionResponseCreator implements ResponseCreator {
private final Exception exception;
@@ -43,7 +43,7 @@ class ExceptionResponseCreator implements ResponseCreator<WebServiceMessage> {
public WebServiceMessage createResponse(URI uri,
WebServiceMessage request,
WebServiceMessageFactory<? extends WebServiceMessage> factory) throws IOException {
WebServiceMessageFactory factory) throws IOException {
if (exception instanceof IOException) {
throw (IOException) exception;
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.test.client;
import org.springframework.util.Assert;
import org.springframework.ws.client.core.WebServiceTemplate;
/**
* @author Arjen Poutsma
*/
public class MockWebServiceServer {
private final MockWebServiceMessageSender mockMessageSender;
private MockWebServiceServer(MockWebServiceMessageSender mockMessageSender) {
Assert.notNull(mockMessageSender, "'mockMessageSender' must not be null");
this.mockMessageSender = mockMessageSender;
}
public static MockWebServiceServer createServer(WebServiceTemplate webServiceTemplate) {
Assert.notNull(webServiceTemplate, "'webServiceTemplate' must not be null");
MockWebServiceMessageSender mockMessageSender = new MockWebServiceMessageSender();
webServiceTemplate.setMessageSender(mockMessageSender);
return new MockWebServiceServer(mockMessageSender);
}
/**
* Records an expectation specified by the given {@link RequestMatcher}. Returns a {@link ResponseActions} object
* that allows for setting up the response, or more expectations.
*
* @param requestMatcher the request matcher expected
* @return the response actions
*/
public ResponseActions expect(RequestMatcher requestMatcher) {
MockSenderConnection connection = mockMessageSender.expectNewConnection();
connection.addRequestMatcher(requestMatcher);
return connection;
}
/**
* Verifies that all connections were used.
*
* @throws AssertionError in case of unused connections.
*/
public void verify() {
mockMessageSender.verifyConnections();
}
}

View File

@@ -22,6 +22,7 @@ import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import org.springframework.ws.WebServiceMessage;
import org.springframework.xml.transform.TransformerHelper;
/**
* Implementation of {@link ResponseCreator} that writes a {@link Source} response.
@@ -29,10 +30,12 @@ import org.springframework.ws.WebServiceMessage;
* @author Arjen Poutsma
* @since 2.0
*/
class PayloadResponseCreator extends AbstractResponseCreator<WebServiceMessage> {
class PayloadResponseCreator extends AbstractResponseCreator {
private final Source payload;
private TransformerHelper transformerHelper = new TransformerHelper();
PayloadResponseCreator(Source payload) {
this.payload = payload;
}
@@ -40,7 +43,7 @@ class PayloadResponseCreator extends AbstractResponseCreator<WebServiceMessage>
@Override
protected void doWithResponse(URI uri, WebServiceMessage request, WebServiceMessage response) throws IOException {
try {
transform(payload, response.getPayloadResult());
transformerHelper.transform(payload, response.getPayloadResult());
}
catch (TransformerException ex) {
throw new AssertionError("Could not transform response payload to message: " + ex.getMessage());

View File

@@ -28,7 +28,7 @@ import org.springframework.ws.WebServiceMessage;
* @author Lukas Krecan
* @since 2.0
*/
public interface RequestMatcher<T extends WebServiceMessage> {
public interface RequestMatcher {
/**
* Matches the given request message against the expectations. Implementations typically make use of JUnit-based
@@ -39,6 +39,6 @@ public interface RequestMatcher<T extends WebServiceMessage> {
* @throws IOException in case of I/O errors
* @throws AssertionError if expectations are not met
*/
void match(URI uri, T request) throws IOException, AssertionError;
void match(URI uri, WebServiceMessage request) throws IOException, AssertionError;
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.test.client;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.test.support.PayloadDiffMatcher;
import org.springframework.xml.transform.ResourceSource;
import org.springframework.xml.validation.XmlValidator;
import org.springframework.xml.validation.XmlValidatorFactory;
/**
* @author Arjen Poutsma
*/
public abstract class RequestMatchers {
private RequestMatchers() {
}
/**
* Expects any request.
*
* @return the request matcher
*/
public static RequestMatcher anything() {
return new RequestMatcher() {
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
}
};
}
/**
* Expects the given {@link javax.xml.transform.Source} XML payload.
*
* @param payload the XML payload
* @return the request matcher
*/
public static RequestMatcher payload(Source payload) {
Assert.notNull(payload, "'payload' must not be null");
final PayloadDiffMatcher matcher = new PayloadDiffMatcher(payload);
return new RequestMatcher() {
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
matcher.match(request);
}
};
}
/**
* Expects the given {@link org.springframework.core.io.Resource} XML payload.
*
* @param payload the XML payload
* @return the request matcher
*/
public static RequestMatcher payload(Resource payload) throws IOException {
Assert.notNull(payload, "'payload' must not be null");
return payload(new ResourceSource(payload));
}
/**
* Expects the payload to validate against the given XSD schema(s).
*
* @param schema the schema
* @param furtherSchemas further schemas, if necessary
* @return the request matcher
*/
public static RequestMatcher validPayload(Resource schema, Resource... furtherSchemas) {
try {
Resource[] joinedSchemas = new Resource[furtherSchemas.length + 1];
joinedSchemas[0] = schema;
System.arraycopy(furtherSchemas, 0, joinedSchemas, 1, furtherSchemas.length);
XmlValidator validator =
XmlValidatorFactory.createValidator(joinedSchemas, XmlValidatorFactory.SCHEMA_W3C_XML);
return new SchemaValidatingRequestMatcher(validator);
}
catch (IOException ex) {
throw new IllegalArgumentException("Schema(s) could not be opened", ex);
}
}
/**
* Expects the given XPath expression to (not) exist or be evaluated to a value.
*
* @param xpathExpression the XPath expression
* @return the XPath expectations, to be further configured
*/
public static XPathExpectations xpath(String xpathExpression) {
return new DefaultXPathExpectations(xpathExpression, null);
}
/**
* Expects the given XPath expression to (not) exist or be evaluated to a value.
*
* @param xpathExpression the XPath expression
* @param namespaceMapping the namespaces
* @return the XPath expectations, to be further configured
*/
public static XPathExpectations xpath(String xpathExpression, Map<String, String> namespaceMapping) {
return new DefaultXPathExpectations(xpathExpression, namespaceMapping);
}
/**
* Expects the given SOAP header in the outgoing message.
*
* @param soapHeaderName the qualified name of the SOAP header to expect
* @return the request matcher
*/
public static RequestMatcher soapHeader(QName soapHeaderName) {
Assert.notNull(soapHeaderName, "'soapHeaderName' must not be null");
return new SoapHeaderMatcher(soapHeaderName);
}
/**
* Expects a connection to the given URI.
*
* @param uri the String uri expected to connect to
* @return the request matcher
*/
public static RequestMatcher connectionTo(String uri) {
Assert.notNull(uri, "'uri' must not be null");
return connectionTo(URI.create(uri));
}
/**
* Expects a connection to the given URI.
*
* @param uri the String uri expected to connect to
* @return the request matcher
*/
public static RequestMatcher connectionTo(URI uri) {
Assert.notNull(uri, "'uri' must not be null");
return new UriMatcher(uri);
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.ws.WebServiceMessageFactory;
* @author Lukas Krecan
* @since 2.0
*/
public interface ResponseCreator<T extends WebServiceMessage> {
public interface ResponseCreator {
/**
* Create a response for the given the request and URI.
@@ -39,6 +39,6 @@ public interface ResponseCreator<T extends WebServiceMessage> {
* @param messageFactory the message that can be used to create responses
* @throws IOException in case of I/O errors
*/
T createResponse(URI uri, T request, WebServiceMessageFactory<? extends T> messageFactory) throws IOException;
WebServiceMessage createResponse(URI uri, WebServiceMessage request, WebServiceMessageFactory messageFactory) throws IOException;
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.test.client;
import java.io.IOException;
import java.util.Locale;
import javax.xml.transform.Source;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.xml.transform.ResourceSource;
/**
* @author Arjen Poutsma
*/
public abstract class ResponseCreators {
private ResponseCreators() {
}
/**
* Respond with the given {@link javax.xml.transform.Source} XML as payload response.
*
* @param payload the response payload
* @return the response callback
*/
public static ResponseCreator withPayload(Source payload) {
Assert.notNull(payload, "'payload' must not be null");
return new PayloadResponseCreator(payload);
}
/**
* Respond with the given {@link org.springframework.core.io.Resource} XML as payload response.
*
* @param payload the response payload
* @return the response callback
*/
public static ResponseCreator withPayload(Resource payload) throws IOException {
Assert.notNull(payload, "'payload' must not be null");
return withPayload(new ResourceSource(payload));
}
/**
* Respond with an error.
*
* @param errorMessage the error message
* @return the response callback
* @see org.springframework.ws.transport.WebServiceConnection#hasError()
* @see org.springframework.ws.transport.WebServiceConnection#getErrorMessage()
*/
public static ResponseCreator withError(String errorMessage) {
Assert.hasLength(errorMessage, "'errorMessage' must not be empty");
return new ErrorResponseCreator(errorMessage);
}
/**
* Respond with an {@link java.io.IOException}.
*
* @param ioException the exception to be thrown
* @return the response callback
*/
public static ResponseCreator withException(IOException ioException) {
Assert.notNull(ioException, "'ioException' must not be null");
return new ExceptionResponseCreator(ioException);
}
/**
* Respond with an {@link RuntimeException}.
*
* @param ex the runtime exception to be thrown
* @return the response callback
*/
public static ResponseCreator withException(RuntimeException ex) {
Assert.notNull(ex, "'ex' must not be null");
return new ExceptionResponseCreator(ex);
}
/**
* Respond with a {@code MustUnderstand} fault.
*
* @param faultStringOrReason the SOAP 1.1 fault string or SOAP 1.2 reason text
* @param locale the language of faultStringOrReason. Optional for SOAP 1.1
* @see org.springframework.ws.soap.SoapBody#addMustUnderstandFault(String, java.util.Locale)
*/
public static ResponseCreator withMustUnderstandFault(String faultStringOrReason, Locale locale) {
Assert.hasLength(faultStringOrReason, "'faultStringOrReason' must not be empty");
return SoapFaultResponseCreator.createMustUnderstandFault(faultStringOrReason, locale);
}
/**
* Respond with a {@code Client} (SOAP 1.1) or {@code Sender} (SOAP 1.2) fault.
*
* @param faultStringOrReason the SOAP 1.1 fault string or SOAP 1.2 reason text
* @param locale the language of faultStringOrReason. Optional for SOAP 1.1
* @see org.springframework.ws.soap.SoapBody#addClientOrSenderFault(String, Locale)
*/
public static ResponseCreator withClientOrSenderFault(String faultStringOrReason, Locale locale) {
Assert.hasLength(faultStringOrReason, "'faultStringOrReason' must not be empty");
return SoapFaultResponseCreator.createClientOrSenderFault(faultStringOrReason, locale);
}
/**
* Respond with a {@code Server} (SOAP 1.1) or {@code Receiver} (SOAP 1.2) fault.
*
* @param faultStringOrReason the SOAP 1.1 fault string or SOAP 1.2 reason text
* @param locale the language of faultStringOrReason. Optional for SOAP 1.1
* @see org.springframework.ws.soap.SoapBody#addServerOrReceiverFault(String, Locale)
*/
public static ResponseCreator withServerOrReceiverFault(String faultStringOrReason, Locale locale) {
Assert.hasLength(faultStringOrReason, "'faultStringOrReason' must not be empty");
return SoapFaultResponseCreator.createServerOrReceiverFault(faultStringOrReason, locale);
}
/**
* Respond with a {@code VersionMismatch} fault.
*
* @param faultStringOrReason the SOAP 1.1 fault string or SOAP 1.2 reason text
* @param locale the language of faultStringOrReason. Optional for SOAP 1.1
* @see org.springframework.ws.soap.SoapBody#addVersionMismatchFault(String, Locale)
*/
public static ResponseCreator withVersionMismatchFault(String faultStringOrReason, Locale locale) {
Assert.hasLength(faultStringOrReason, "'faultStringOrReason' must not be empty");
return SoapFaultResponseCreator.createVersionMismatchFault(faultStringOrReason, locale);
}
}

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import java.net.URI;
import java.util.Locale;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapMessage;
@@ -31,11 +32,16 @@ import static org.springframework.ws.test.support.AssertionErrors.fail;
* @author Arjen Poutsma
* @since 2.0
*/
abstract class SoapFaultResponseCreator extends AbstractResponseCreator<SoapMessage> {
abstract class SoapFaultResponseCreator extends AbstractResponseCreator {
@Override
protected void doWithResponse(URI uri, SoapMessage request, SoapMessage response) throws IOException {
SoapBody responseBody = response.getSoapBody();
protected void doWithResponse(URI uri, WebServiceMessage request, WebServiceMessage response) throws IOException {
if (!(response instanceof SoapMessage)) {
fail("Response is not a SOAP message");
return;
}
SoapMessage soapResponse = (SoapMessage) response;
SoapBody responseBody = soapResponse.getSoapBody();
if (responseBody == null) {
fail("SOAP message [" + response + "] does not contain SOAP body");
}

View File

@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.test.client.MockWebServiceServer;
import org.springframework.ws.test.integration.CustomerCountRequest;
import org.springframework.ws.test.integration.CustomerCountResponse;
import org.springframework.xml.transform.StringSource;
@@ -31,7 +32,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.springframework.ws.test.client.WebServiceMock.*;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;
/**
* Integration test for client-side WebService testing. In different package so we can't use the package-protected
@@ -46,9 +48,11 @@ public class ClientIntegrationTest {
@Autowired
private WebServiceTemplate webServiceTemplate;
private MockWebServiceServer mockServer;
@Before
public void setUpMocks() throws Exception {
mockWebServiceTemplate(webServiceTemplate);
public void createServer() throws Exception {
mockServer = MockWebServiceServer.createServer(webServiceTemplate);
}
@Test
@@ -60,7 +64,7 @@ public class ClientIntegrationTest {
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
"<customerCount>10</customerCount>" + "</customerCountResponse>");
expect(payload(expectedRequestPayload)).andRespond(withPayload(responsePayload));
mockServer.expect(payload(expectedRequestPayload)).andRespond(withPayload(responsePayload));
CustomerCountRequest request = new CustomerCountRequest();
request.setCustomerName("John Doe");
@@ -68,7 +72,7 @@ public class ClientIntegrationTest {
CustomerCountResponse response = (CustomerCountResponse) webServiceTemplate.marshalSendAndReceive(request);
assertEquals(10, response.getCustomerCount());
verifyConnections();
mockServer.verify();
}
}