我迟到了三年多,但是我花了一段时间来解决同样的问题,所以我虽然值得记录我的解决方案作为答案,这样它可能会让其他人免于从头开始手动处理SOAP有效负载的头痛。
我做了一个合理的研究,试图为我的集成测试套件解决这个问题。尝试了各种方法,包括 CXF 自定义生成的服务器、SOAP-UI、受 CGLIB 影响的库,可在测试上下文中替换真正的客户端。
我最终使用WireMock和自定义请求匹配器来处理所有的-yness。SOAP
它的要点是一个类,它处理 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");