繁体   English   中英

在 Java 中将 WireMock 与 SOAP Web 服务结合使用

[英]Using WireMock with SOAP Web Services in Java

我对WireMock完全陌生

到目前为止,我一直在使用 SOAPUI 使用模拟响应。 我的用例很简单:

只需将 SOAP XML 请求发送到不同的端点 ( http://localhost:9001/endpoint1 ) 并获取罐头 XML 响应。 但是 MockWrire 必须作为独立服务部署到专用服务器上,该服务器将充当提供模拟响应的中心位置。

只是想要一些开始的建议。 正如我所见,WireMock 更适合 REST Web 服务。 所以我的疑问是:

1) 我是否需要将它部署到 Java Web 服务器或容器以始终运行独立服务。 我读到你可以通过使用

java -jar mockwire.jar --port [port_number]

2) 我需要使用 MockWire APIs 吗? 我需要为我的用例创建类吗? 就我而言,请求将通过 JUnit 测试用例触发以进行模拟。

3)如何实现简单的URL模式匹配? 如上所述,我只需要简单的模拟,即在向http://localhost:9001/endpoint1发出请求时获得响应

4)我的用例有更好/更简单的框架吗? 我读过 Mockable,但它对 3 个团队成员和免费层的演示域有限制。

我是 WireMock 的创造者。

我最近使用 WireMock 在客户端项目上模拟了一组 SOAP 接口,因此我可以证明这是可能的。 至于它比 SOAP UI 好还是坏,我想说有一些明确的好处,但也有一些权衡。 一个主要的好处是部署和编程访问/配置相对容易,并且支持诸如 HTTPS 和低级故障注入之类的东西。 但是,您需要做更多的工作来解析和生成 SOAP 有效负载 - 它不会像 SOAP UI 那样从 WSDL 生成代码/存根。

我的经验是,像 SOAP UI 这样的工具会让你更快地开始,但从长远来看,当你的测试套件变得不重要时,往往会导致更高的维护成本。

依次解决您的问题: 1) 如果您希望模拟在某个服务器上运行,最简单的方法是运行您所描述的独立 JAR。 我建议不要尝试将其部署到容器中 - 此选项仅在别无选择的情况下才存在。

但是,如果您只想运行集成测试或完全独立的功能测试,我建议使用 JUnit 规则。 我会说在专用进程中运行它只是一个好主意,如果 a) 您将其他已部署的系统插入其中,或者 b) 您从非 JVM 语言使用它。

2) 您需要以 3 种方式之一配置它:1) Java API,2) JSON over HTTP,或 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 ServerBetamax都是 WireMock 的成熟替代品,但 AFAIK 它们不提供任何更明确的 SOAP 支持。

我参加这个聚会晚了三年多,但我花了一段时间来解决同样的问题,所以我认为值得记录我的解决方案作为答案,这样它可能会为其他人省去手动处理 SOAP 有效负载的麻烦划痕。

我做了一些合理的研究,试图为我的集成测试套件解决这个问题。 尝试了各种各样的东西,包括 CXF 自定义生成的服务器、SOAP-UI、一个受 CGLIB 影响的库,它在测试上下文中替换了真实的客户端。

我最终使用带有自定义请求匹配器的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");
  1. 我将wiremock服务器作为独立运行
  2. 我创建了一个 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" }}}

  3. 我创建了一个响应 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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM