SWS-632 - Separated MockWebServiceClient logic into RequestCreators and ResponseMatchers

This commit is contained in:
Arjen Poutsma
2010-11-02 11:13:51 +00:00
parent cea6c75cc4
commit 83ce414350
9 changed files with 263 additions and 135 deletions

View File

@@ -1,37 +0,0 @@
/*
* 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.server;
import java.io.IOException;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
/**
* @author Arjen Poutsma
*/
abstract class AbstractRequestCreator implements RequestCreator {
public final WebServiceMessage createRequest(WebServiceMessageFactory messageFactory) throws IOException {
WebServiceMessage request = messageFactory.createWebServiceMessage();
doWithRequest(request);
return request;
}
protected abstract void doWithRequest(WebServiceMessage request) throws IOException;
}

View File

@@ -18,11 +18,9 @@ package org.springframework.ws.test.server;
import java.io.IOException;
import java.util.Map;
import javax.xml.transform.Source;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.ws.WebServiceMessage;
@@ -31,9 +29,7 @@ import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.soap.server.SoapMessageDispatcher;
import org.springframework.ws.test.support.PayloadDiffMatcher;
import org.springframework.ws.transport.WebServiceMessageReceiver;
import org.springframework.xml.transform.ResourceSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -41,7 +37,30 @@ import org.apache.commons.logging.LogFactory;
import static org.springframework.ws.test.support.Assert.fail;
/**
* <strong>Main entry point for server-side Web service testing</strong>. Typically used to test a {@link
* org.springframework.ws.server.MessageDispatcher MessageDispatcher} (including its endpoints, mappings, etc) by
* creating request messages, and setting up expectations about response messages.
* <p/>
* The typical usage of this class is:
* <ol>
* <li>Create a {@code MockWebServiceClient} instance by using {@link #createClient(ApplicationContext)} or
* {@link #createClient(WebServiceMessageReceiver, WebServiceMessageFactory)}</li>
* <li>Send request messages by calling {@link #sendMessage(RequestCreator)}, possibly by using the default
* {@link RequestCreator} implementations provided in {@link RequestCreators} (which can be statically imported).</li>
* <li>Set up response expectations by calling {@link ResponseActions#andExpect(ResponseMatcher) andExpect(ResponseMatcher)},
* possibly by using the default {@link ResponseMatcher} implementations provided in {@link ResponseMatchers}
* (which can be statically imported). Multiple expectations can be set up by chaining {@code andExpect()} calls.</li>
* </ol>
* Note that because of the 'fluent' API offered by this class (and related classes), you can typically use the Code
* Completion features (i.e. ctrl-space) in your IDE to set up the mocks.
* <p/>
* For example:
* <blockquote><pre>
* </pre></blockquote>
*
* @author Arjen Poutsma
* @author Lukas Krecan
* @since 2.0
*/
public class MockWebServiceClient {
@@ -51,6 +70,8 @@ public class MockWebServiceClient {
private final WebServiceMessageFactory messageFactory;
// Constructors
private MockWebServiceClient(WebServiceMessageReceiver messageReceiver, WebServiceMessageFactory messageFactory) {
Assert.notNull(messageReceiver, "'messageReceiver' must not be null");
Assert.notNull(messageFactory, "'messageFactory' must not be null");
@@ -58,15 +79,36 @@ public class MockWebServiceClient {
this.messageFactory = messageFactory;
}
// Constructors
// Factory methods
/**
* Creates a {@code MockWebServiceClient} instance based on the given {@link WebServiceMessageReceiver} and {@link
* WebServiceMessageFactory}.
*
* @param messageReceiver the message receiver, typically a {@link SoapMessageDispatcher}
* @param messageFactory the message factory
* @return the created client
*/
public static MockWebServiceClient createClient(WebServiceMessageReceiver messageReceiver,
WebServiceMessageFactory messageFactory) {
return new MockWebServiceClient(messageReceiver, messageFactory);
}
// Factory methods
/**
* Creates a {@code MockWebServiceClient} instance based on the given {@link ApplicationContext}.
*
* This factory method works in a similar fashion as the standard
* {@link org.springframework.ws.transport.http.MessageDispatcherServlet MessageDispatcherServlet}. That is:
* <ul>
* <li>If a {@link WebServiceMessageReceiver} is configured in the given application context, it will use that.
* If no message receiver is configured, it will create a default {@link SoapMessageDispatcher}.</li>
* <li>If a {@link WebServiceMessageFactory} is configured in the given application context, it will use that.
* If no message factory is configured, it will create a default {@link SaajSoapMessageFactory}.</li>
* </ul>
*
* @param applicationContext the application context to base the client on
* @return the created client
*/
public static MockWebServiceClient createClient(ApplicationContext applicationContext) {
WebServiceMessageReceiver messageReceiver = getMessageReceiver(applicationContext);
WebServiceMessageFactory messageFactory = getMessageFactory(applicationContext);
@@ -120,6 +162,14 @@ public class MockWebServiceClient {
// Sending
/**
* Sends a request message by using the given {@link RequestCreator}. Typically called by using the default request
* creators provided by {@link RequestCreators}.
*
* @param requestCreator the request creator
* @return the response actions
* @see RequestCreators
*/
public ResponseActions sendMessage(RequestCreator requestCreator) {
Assert.notNull(requestCreator, "'requestCreator' must not be null");
try {
@@ -128,7 +178,7 @@ public class MockWebServiceClient {
messageReceiver.receive(messageContext);
return new MockWebServiceExchange(messageContext);
return new MockWebServiceClientResponseActions(messageContext);
}
catch (Exception ex) {
fail(ex.getMessage());
@@ -136,21 +186,13 @@ public class MockWebServiceClient {
}
}
public ResponseActions sendPayload(Source payload) {
Assert.notNull(payload, "'payload' must not be null");
return sendMessage(new PayloadRequestCreator(payload));
}
// ResponseActions
public ResponseActions sendPayload(Resource payload) throws IOException {
Assert.notNull(payload, "'payload' must not be null");
return sendMessage(new PayloadRequestCreator(new ResourceSource(payload)));
}
private class MockWebServiceExchange implements ResponseActions {
private static class MockWebServiceClientResponseActions implements ResponseActions {
private final MessageContext messageContext;
private MockWebServiceExchange(MessageContext messageContext) {
private MockWebServiceClientResponseActions(MessageContext messageContext) {
Assert.notNull(messageContext, "'messageContext' must not be null");
this.messageContext = messageContext;
}
@@ -170,19 +212,6 @@ public class MockWebServiceClient {
return null;
}
}
public ResponseActions andExpectPayload(Source payload) {
final PayloadDiffMatcher matcher = new PayloadDiffMatcher(payload);
return andExpect(new ResponseMatcher() {
public void match(WebServiceMessage response) throws IOException, AssertionError {
matcher.match(response);
}
});
}
public ResponseActions andExpectPayload(Resource payload) throws IOException {
return andExpectPayload(new ResourceSource(payload));
}
}

View File

@@ -1,52 +0,0 @@
/*
* 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.server;
import java.io.IOException;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import org.springframework.ws.WebServiceMessage;
import org.springframework.xml.transform.TransformerHelper;
/**
* Implementation of {@link org.springframework.ws.test.client.ResponseCreator} that writes a {@link
* javax.xml.transform.Source} response.
*
* @author Arjen Poutsma
* @since 2.0
*/
class PayloadRequestCreator extends AbstractRequestCreator {
private final Source payload;
private TransformerHelper transformerHelper = new TransformerHelper();
PayloadRequestCreator(Source payload) {
this.payload = payload;
}
@Override
protected void doWithRequest(WebServiceMessage request) throws IOException {
try {
transformerHelper.transform(payload, request.getPayloadResult());
}
catch (TransformerException ex) {
throw new AssertionError("Could not transform request payload to message: " + ex.getMessage());
}
}
}

View File

@@ -22,7 +22,11 @@ import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
/**
* Creates request messages. Implementations of this interface are returned by {@link RequestCreators}.
*
* @author Arjen Poutsma
* @see RequestCreators
* @since 2.0
*/
public interface RequestCreator {
@@ -30,7 +34,7 @@ public interface RequestCreator {
* Create a request.
*
* @param messageFactory the message that can be used to create responses
* @throws java.io.IOException in case of I/O errors
* @throws IOException in case of I/O errors
*/
WebServiceMessage createRequest(WebServiceMessageFactory messageFactory) throws IOException;

View File

@@ -0,0 +1,107 @@
/*
* 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.server;
import java.io.IOException;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.xml.transform.ResourceSource;
import org.springframework.xml.transform.TransformerHelper;
/**
* Factory methods for {@link RequestCreator} classes. Typically used to provide input for {@link
* MockWebServiceClient#sendMessage(RequestCreator)}.
*
* @author Arjen Poutsma
* @since 2.0
*/
public abstract class RequestCreators {
private RequestCreators() {
}
/**
* Create a request with the given {@link Source} XML as payload.
*
* @param payload the request payload
* @return the request creator
*/
public static RequestCreator withPayload(Source payload) {
Assert.notNull(payload, "'payload' must not be null");
return new PayloadRequestCreator(payload);
}
/**
* Create a request with the given {@link Resource} XML as payload.
*
* @param payload the request payload
* @return the request creator
*/
public static RequestCreator withPayload(Resource payload) throws IOException {
Assert.notNull(payload, "'payload' must not be null");
return withPayload(new ResourceSource(payload));
}
/**
* Abstract base class for the {@link RequestCreator} interface.
* <p/>
* Creates a response using the given {@link org.springframework.ws.WebServiceMessageFactory}, and passes it on to
* {@link #doWithRequest(org.springframework.ws.WebServiceMessage)}.
*/
private static abstract class AbstractRequestCreator implements RequestCreator {
public final WebServiceMessage createRequest(WebServiceMessageFactory messageFactory) throws IOException {
WebServiceMessage request = messageFactory.createWebServiceMessage();
doWithRequest(request);
return request;
}
protected abstract void doWithRequest(WebServiceMessage request) throws IOException;
}
/**
* Implementation of {@link RequestCreator} that creates a request based on a {@link javax.xml.transform.Source}.
*/
private static class PayloadRequestCreator extends AbstractRequestCreator {
private final Source payload;
private TransformerHelper transformerHelper = new TransformerHelper();
PayloadRequestCreator(Source payload) {
this.payload = payload;
}
@Override
protected void doWithRequest(WebServiceMessage request) throws IOException {
try {
transformerHelper.transform(payload, request.getPayloadResult());
}
catch (TransformerException ex) {
throw new AssertionError("Could not transform request payload to message: " + ex.getMessage());
}
}
}
}

View File

@@ -16,26 +16,21 @@
package org.springframework.ws.test.server;
import java.io.IOException;
import javax.xml.transform.Source;
import org.springframework.core.io.Resource;
/**
* Allows for setting up expectation about response messages. Implementations of this interface are returned by
* {@link MockWebServiceClient#sendMessage(RequestCreator)}.
*
* @author Arjen Poutsma
* @since 2.0
*/
public interface ResponseActions {
/**
* Allows for further expectations to be set on the request.
* Sets up an expectation about the response message.
*
* @return the request expectations
* @param responseMatcher the response matcher that defines expectations
* @return an instance of {@link ResponseActions}, to set up further expectations
*/
ResponseActions andExpect(ResponseMatcher responseMatcher);
ResponseActions andExpectPayload(Source payload);
ResponseActions andExpectPayload(Resource payload) throws IOException;
}

View File

@@ -21,6 +21,9 @@ import java.io.IOException;
import org.springframework.ws.WebServiceMessage;
/**
* Defines the contract for matching response messages to expectations. Implementations of this interface are returned
* by {@link ResponseMatchers}.
*
* @author Arjen Poutsma
* @since 2.0
*/

View File

@@ -0,0 +1,76 @@
/*
* 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.server;
import java.io.IOException;
import javax.xml.transform.Source;
import org.springframework.core.io.Resource;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.test.support.PayloadDiffMatcher;
import org.springframework.xml.transform.ResourceSource;
/**
* Factory methods for {@link ResponseMatcher} classes. Typically used to provide input for {@link
* ResponseActions#andExpect(ResponseMatcher)}.
*
* @author Arjen Poutsma
* @since 2.0
*/
public abstract class ResponseMatchers {
private ResponseMatchers() {
}
/**
* Expects any response.
*
* @return the response matcher
*/
public static ResponseMatcher anything() {
return new ResponseMatcher() {
public void match(WebServiceMessage response) {
}
};
}
/**
* Expects the given {@link Source} XML payload.
*
* @param payload the XML payload
* @return the response matcher
*/
public static ResponseMatcher payload(Source payload) {
final PayloadDiffMatcher matcher = new PayloadDiffMatcher(payload);
return new ResponseMatcher() {
public void match(WebServiceMessage response) throws IOException {
matcher.match(response);
}
};
}
/**
* Expects the given {@link Resource} XML payload.
*
* @param payload the XML payload
* @return the response matcher
*/
public ResponseMatcher payload(Resource payload) throws IOException {
return payload(new ResourceSource(payload));
}
}

View File

@@ -29,6 +29,9 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.springframework.ws.test.server.RequestCreators.withPayload;
import static org.springframework.ws.test.server.ResponseMatchers.payload;
/**
* @author Arjen Poutsma
*/
@@ -54,7 +57,7 @@ public class ServerIntegrationTest {
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
"<customerCount>42</customerCount>" + "</customerCountResponse>");
mockClient.sendPayload(requestPayload).andExpectPayload(responsePayload);
mockClient.sendMessage(withPayload(requestPayload)).andExpect(payload(responsePayload));
}