在 Java 中将 WireMock 与 SOAP Web 服务结合使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35974249/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using WireMock with SOAP Web Services in Java
提问by Anurag
I am totally new to WireMock.
Until now, I have been using mock responses using SOAPUI. My use case is simple:
到目前为止,我一直在使用 SOAPUI 使用模拟响应。我的用例很简单:
Just firing SOAP XML requests to different endpoints (http://localhost:9001/endpoint1) and getting canned XML response back. But MockWrire has to be deployed as a standalone service onto a dedicated server which will act a central location from where mock responses will be served.
只需将 SOAP XML 请求发送到不同的端点 ( http://localhost:9001/endpoint1) 并获取罐头 XML 响应。但是 MockWrire 必须作为独立服务部署到专用服务器上,该服务器将充当提供模拟响应的中心位置。
Just wanted some starting suggestions. As I can see WireMock is more suitable towards REST web services. So my doubts are:
只是想要一些开始的建议。正如我所看到的,WireMock 更适合 REST Web 服务。所以我的疑问是:
1) Do I need to deploy it to a java web server or container to act as always running standalone service. I read that you can just spin off by using
1) 我是否需要将它部署到 Java Web 服务器或容器以始终运行独立服务。我读到你可以通过使用
java -jar mockwire.jar --port [port_number]
2) Do I need to use MockWire APIs? Do I need to make classes for my use case? In my case, requests will be triggered via JUnit test cases for mocking.
2) 我需要使用 MockWire APIs 吗?我需要为我的用例创建类吗?就我而言,请求将通过 JUnit 测试用例触发以进行模拟。
3) How do I achieve simple URL pattern matching? As stated above, I just need simple mocking i.e get response when request is made to http://localhost:9001/endpoint1
3)如何实现简单的URL模式匹配?如上所述,我只需要简单的模拟,即在向http://localhost:9001/endpoint1发出请求时获得响应
4) Is there a better/easier framework for my usecase? I read about Mockable but it has restrictions for 3 team members and demo domain in free tier.
4)我的用例有更好/更简单的框架吗?我读过 Mockable,但它对 3 个团队成员和免费层的演示域有限制。
采纳答案by Tom
I'm WireMock's creator.
我是 WireMock 的创造者。
I've used WireMock to mock a collection of SOAP interfaces on a client project quite recently, so I can attest that it's possible. As for whether it's better or worse than SOAP UI, I'd say there are some definite upsides, but with some tradeoffs. A major benefit is the relative ease of deployment and programmatic access/configuration, and support for things like HTTPS and low-level fault injection. However, you need to do a bit more work to parse and generate SOAP payloads - it won't do code/stub generation from WSDL like SOAP UI will.
我最近使用 WireMock 在客户端项目上模拟了一组 SOAP 接口,因此我可以证明这是可能的。至于它比 SOAP UI 好还是坏,我想说有一些明确的好处,但也有一些权衡。一个主要的好处是部署和编程访问/配置相对容易,并且支持诸如 HTTPS 和低级故障注入之类的东西。但是,您需要做更多的工作来解析和生成 SOAP 有效负载 - 它不会像 SOAP UI 那样从 WSDL 生成代码/存根。
My experience is that tools like SOAP UI will get you started faster, but tend to result in higher maintenance costs in the long run when your test suite grows beyond trivial.
我的经验是,像 SOAP UI 这样的工具会让你更快地开始,但从长远来看,当你的测试套件变得不重要时,往往会导致更高的维护成本。
To address your points in turn: 1) If you want your mocks to run on a server somewhere, the easiest way to do this is to run the standalone JAR as you've described. I'd advise against trying to deploy it to a container - this option really only exists for when there's no alternative.
依次解决您的问题: 1) 如果您希望模拟在某个服务器上运行,最简单的方法是运行您所描述的独立 JAR。我建议不要尝试将其部署到容器中 - 此选项仅在别无选择的情况下才存在。
However, if you just want to run integration tests or totally self-contained functional tests, I'd suggest using the JUnit rule. I'd say it's only a good idea to run it in a dedicated process if either a) you're plugging other deployed systems into it, or b) you're using it from a non-JVM language.
但是,如果您只想运行集成测试或完全独立的功能测试,我建议使用 JUnit 规则。我会说在专用进程中运行它只是一个好主意,如果 a) 您将其他已部署的系统插入其中,或者 b) 您从非 JVM 语言使用它。
2) You'd need to configure it in one of 3 ways 1) the Java API, 2) JSON over HTTP, or 3) JSON files. 3) is probably closest to what you're used to with SOAP UI.
2) 您需要以 3 种方式之一配置它:1) Java API,2) JSON over HTTP,或 3) JSON 文件。3) 可能最接近您习惯的 SOAP UI。
3) See http://wiremock.org/stubbing.htmlfor lots of stubbing examples using both JSON and Java. Since SOAP tends to bind to fixed endpoint URLs, you probably want urlEqualTo(...)
. When I've stubbed SOAP in the past I've tended to XML match on the entire request body (see http://wiremock.org/stubbing.html#xml-body-matching). I'd suggest investing in writing a few Java builders to emit the request and response body XML you need.
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 Serverand Betamaxare both mature alternatives to WireMock, but AFAIK they don't offer any more explicit SOAP support.
4) Mock Server和Betamax都是 WireMock 的成熟替代品,但 AFAIK 它们不提供任何更明确的 SOAP 支持。
回答by markdsievers
I'm over three years late to this party, but it took me a while to work through the same problem so I though it worthy of documenting my solution as an answer so it might save someone else the headache of manually dealing with SOAP payloads from scratch.
我参加这个聚会晚了三年多,但我花了一段时间来解决同样的问题,所以我认为值得记录我的解决方案作为答案,这样它可能会为其他人省去手动处理 SOAP 有效负载的麻烦划痕。
I did a reasonable about of research trying to solve this problem for my integration test suite. Tried all sorts of things including CXF custom generated servers, SOAP-UI, a CGLIB influenced library that replaces the real client in a test context.
我做了一些合理的研究,试图为我的集成测试套件解决这个问题。尝试了各种各样的东西,包括 CXF 自定义生成的服务器、SOAP-UI、一个受 CGLIB 影响的库,它在测试上下文中替换了真实的客户端。
I ended up using WireMock with custom request matchersto handle all the SOAP
-yness.
我最终使用带有自定义请求匹配器的SOAP
WireMock来处理所有的-yness。
The gist of it was a class that handled unmarshaling of SOAP requests and marshaling of SOAP responses in order to provide a convenient wrapper to test authors that only required JAXB generated objects and never had to concern themselves with the details of SOAP.
它的要点是一个处理 SOAP 请求的解组和 SOAP 响应的编组的类,以便为只需要 JAXB 生成的对象而不必关心 SOAP 细节的测试作者提供方便的包装器。
Response Marshaling
响应编组
/**
* 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();
}
Request Unmarshaling
请求解组
/**
* 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;
}
In addition to this was some XPath utilities for peeking into the request payload and looking at what operation was being requested.
除此之外,还有一些 XPath 实用程序,用于查看请求有效负载并查看请求的操作。
All the SOAP handling was the fiddliest part to get working. From there it's just creating your own API to supplement WireMocks. For example
所有的 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))));
}
and as a result you end up with a nice, lean tests.
结果你得到了一个很好的、精益的测试。
SoapContext context = new SoapContext(...) // URIs, QName, Prefix, ect
context.stubOperation("createUser", CreateUser.class, (u) -> "myUser".equals(u.getUserName()), new CreateUserResponse());
soapClient.createUser("myUser");
回答by Jin
- I running the wiremock server as standalone
I create a mapping.json file, put in my mock project 'mappings' folder
{"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" }}}
I create a response xml file, put it in the '__files' folder
<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>
- 我将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>