在 Java 中将 WireMock 与 SOAP Web 服务一起使用
Posted
技术标签:
【中文标题】在 Java 中将 WireMock 与 SOAP Web 服务一起使用【英文标题】:Using WireMock with SOAP Web Services in Java 【发布时间】:2016-03-13 18:46:12 【问题描述】:我对@987654321@ 完全陌生。
到目前为止,我一直在使用 SOAPUI 模拟响应。我的用例很简单:
只需将 SOAP XML 请求发送到不同的端点 (http://localhost:9001/endpoint1) 并返回预设的 XML 响应。但是 MockWrire 必须作为独立服务部署到专用服务器上,该服务器将充当提供模拟响应的中心位置。
只是想要一些开始的建议。正如我所见,WireMock 更适合 REST Web 服务。所以我的疑问是:
1) 我是否需要将其部署到 Java Web 服务器或容器以充当始终运行的独立服务。我读到你可以通过使用来分拆
java -jar mockwire.jar --port [port_number]
2) 我需要使用 MockWire API 吗?我需要为我的用例制作课程吗?就我而言,请求将通过 JUnit 测试用例触发以进行模拟。
3) 如何实现简单的 URL 模式匹配?如上所述,我只需要简单的模拟即可,即在向http://localhost:9001/endpoint1 发出请求时获得响应
4) 我的用例是否有更好/更简单的框架?我读过 Mockable,但它对 3 名团队成员和免费层的演示域有限制。
【问题讨论】:
github.com/skjolber/mockito-soap-cxf 【参考方案1】:我是 WireMock 的创造者。
我最近使用 WireMock 在客户端项目上模拟了一组 SOAP 接口,因此我可以证明这是可能的。至于它是否比 SOAP UI 更好或更差,我想说有一些明确的好处,但也有一些权衡。一个主要的好处是相对容易部署和编程访问/配置,以及对 HTTPS 和低级故障注入等内容的支持。但是,您需要做更多的工作来解析和生成 SOAP 有效负载 - 它不会像 SOAP UI 那样从 WSDL 生成代码/存根。
我的经验是,像 SOAP UI 这样的工具可以让您更快地开始,但从长远来看,当您的测试套件变得微不足道时,往往会导致更高的维护成本。
依次解决您的观点: 1)如果您希望您的模拟在某处的服务器上运行,最简单的方法是运行您所描述的独立 JAR。我建议不要尝试将其部署到容器中 - 此选项仅在别无选择时才存在。
但是,如果您只想运行集成测试或完全独立的功能测试,我建议您使用 JUnit 规则。我想说在专用进程中运行它只是一个好主意,如果 a) 您正在将其他已部署的系统插入其中,或者 b) 您正在从非 JVM 语言中使用它。
2) 您需要以 3 种方式之一对其进行配置:1) Java API,2) 基于 HTTP 的 JSON,或 3) JSON 文件。 3) 可能最接近您使用 SOAP UI 的习惯。
3) 有关使用 JSON 和 Java 的大量存根示例,请参阅 http://wiremock.org/stubbing.html。由于 SOAP 倾向于绑定到固定的端点 URL,您可能需要urlEqualTo(...)
。过去当我使用 SOAP 存根时,我倾向于在整个请求正文上进行 XML 匹配(请参阅 http://wiremock.org/stubbing.html#xml-body-matching)。我建议投资编写一些 Java 构建器来发出您需要的请求和响应正文 XML。
4) Mock Server 和 Betamax 都是 WireMock 的成熟替代品,但 AFAIK 它们不提供任何更明确的 SOAP 支持。
【讨论】:
您能否解释一下,如何指定“__files”和“mappings”文件夹使用哪个相对/绝对路径?我已经通过 maven 设置了 mockwire 依赖项,并创建了一个简单的 servlet 来在 servletinit()
上执行 wireMockServer.start();
。我猜withRootDirectory()
或usingFilesUnderDirectory()
和WireMockConfiguration
应该可以工作吗?
我通过创建自定义FileSource
并将其传递给WireMockConfiguration
解决了这个问题。谢谢。
@Tom 你在 *** 上不是很丰富,但我只想说 WireMock 是一个很棒的工具包——非常感谢!
Tom,你能提供一些例子吗?
@RafalEnden 有一个工作示例所需的所有代码都在我下面的答案中。【参考方案2】:
我迟到了三年多,但我花了一段时间来解决同样的问题,所以我认为值得记录我的解决方案作为答案,这样它可能会省去其他人手动处理的麻烦从头开始 SOAP 有效负载。
我做了一些合理的研究,试图为我的集成测试套件解决这个问题。尝试了各种各样的东西,包括 CXF 自定义生成的服务器、SOAP-UI、受 CGLIB 影响的库,它在测试上下文中替换了真实的客户端。
我最终使用带有custom request matchers 的WireMock 来处理所有SOAP
-yness。
它的要点是一个处理 SOAP 请求的解组和 SOAP 响应的编组的类,以便为只需要 JAXB 生成的对象而不必关心 SOAP 细节的测试作者提供方便的包装器。
响应编组
/**
* Accepts a WebService response object (as defined in the WSDL) and marshals
* to a SOAP envelope String.
*/
public <T> String serializeObject(T object)
ByteArrayOutputStream byteArrayOutputStream;
Class clazz = object.getClass();
String responseRootTag = StringUtils.uncapitalize(clazz.getSimpleName());
QName payloadName = new QName("your_namespace_URI", responseRootTag, "namespace_prefix");
try
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
Marshaller objectMarshaller = jaxbContext.createMarshaller();
JAXBElement<T> jaxbElement = new JAXBElement<>(payloadName, clazz, null, object);
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
objectMarshaller.marshal(jaxbElement, document);
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
SOAPBody body = soapMessage.getSOAPPart().getEnvelope().getBody();
body.addDocument(document);
byteArrayOutputStream = new ByteArrayOutputStream();
soapMessage.saveChanges();
soapMessage.writeTo(byteArrayOutputStream);
catch (Exception e)
throw new RuntimeException(String.format("Exception trying to serialize [%s] to a SOAP envelope", object), e);
return byteArrayOutputStream.toString();
请求解组
/**
* Accepts a WebService request object (as defined in the WSDL) and unmarshals
* to the supplied type.
*/
public <T> T deserializeSoapRequest(String soapRequest, Class<T> clazz)
XMLInputFactory xif = XMLInputFactory.newFactory();
JAXBElement<T> jb;
try
XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(soapRequest));
// Advance the tag iterator to the tag after Body, eg the start of the SOAP payload object
do
xsr.nextTag();
while(!xsr.getLocalName().equals("Body"));
xsr.nextTag();
JAXBContext jc = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = jc.createUnmarshaller();
jb = unmarshaller.unmarshal(xsr, clazz);
xsr.close();
catch (Exception e)
throw new RuntimeException(String.format("Unable to deserialize request to type: %s. Request \n %s", clazz, soapRequest), e);
return jb.getValue();
private XPath getXPathFactory()
Map<String, String> namespaceUris = new HashMap<>();
namespaceUris.put("xml", XMLConstants.XML_NS_URI);
namespaceUris.put("soap", "http://schemas.xmlsoap.org/soap/envelope/");
// Add additional namespaces to this map
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new NamespaceContext()
public String getNamespaceURI(String prefix)
if (namespaceUris.containsKey(prefix))
return namespaceUris.get(prefix);
else
return XMLConstants.NULL_NS_URI;
public String getPrefix(String uri)
throw new UnsupportedOperationException();
public Iterator getPrefixes(String uri)
throw new UnsupportedOperationException();
);
return xpath;
除此之外,还有一些 XPath 实用程序可用于查看请求负载并查看正在请求的操作。
所有 SOAP 处理都是最繁琐的部分。从那里开始,它只是创建您自己的 API 来补充 WireMocks。例如
public <T> void stubOperation(String operation, Class<T> clazz, Predicate<T> predicate, Object response)
wireMock.stubFor(requestMatching(
new SoapObjectMatcher<>(context, clazz, operation, predicate))
.willReturn(aResponse()
.withHeader("Content-Type", "text/xml")
.withBody(serializeObject(response))));
因此,您最终得到了一个不错的精益测试。
SoapContext context = new SoapContext(...) // URIs, QName, Prefix, ect
context.stubOperation("createUser", CreateUser.class, (u) -> "myUser".equals(u.getUserName()), new CreateUserResponse());
soapClient.createUser("myUser");
【讨论】:
太棒了,谢谢分享!我提交了github.com/tomakehurst/wiremock/issues/759 以将此添加到 WireMock。 我有问题。我有肥皂 1.2。我创建了端点,但它给了我错误:soap 1.2 消息在发送到仅限soap 1.1 的端点时无效。任何帮助,将不胜感激。谢谢!【参考方案3】:-
我将wiremock服务器作为独立服务器运行
我创建了一个 mapping.json 文件,放在我的模拟项目'mappings'文件夹中
"request":
"url": "/webservicesserver/numberconversion",
"method": "POST",
"response":
"status": 200,
"bodyFileName": "response.xml",
"headers":
"Server": "Microsoft-IIS/8.0",
"Access-Control-Allow-Origin": "http://www.dataaccess.com",
"Access-Control-Allow-Methods": "GET, POST",
"Connection": "Keep-Alive",
"Web-Service": "DataFlex 18.1",
"Access-Control-Allow-Headers": "content-type",
"Date": "Tue, 26 Jun 2018 07:45:47 GMT",
"Strict-Transport-Security": "max-age=31536000",
"Cache-Control": "private, max-age=0",
"Access-Control-Allow-Credentials": true,
"Content-Length": 352,
"Content-Type": "application/soap+xml; charset=utf-8"
我创建了一个响应xml文件,把它放在'__files'文件夹中
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" >
<soap:Body>
<m:NumberToDollarsResponse xmlns:m="http://www.dataaccess.com/webservicesserver/">
<m:NumberToDollarsResult>twelve dollars</m:NumberToDollarsResult>
</m:NumberToDollarsResponse>
</soap:Body>
</soap:Envelope>
【讨论】:
以上是关于在 Java 中将 WireMock 与 SOAP Web 服务一起使用的主要内容,如果未能解决你的问题,请参考以下文章