在Java中使用WireMock和SOAP Web服务

Anu*_*rag 12 java soap web-services mocking wiremock

我对WireMock完全不熟悉.

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

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

只想要一些开始的建议.我可以看到WireMock更适合REST Web服务.所以我的怀疑是:

1)我是否需要将其部署到Java Web服务器或容器以充当始终运行的独立服务.我读到你可以通过使用来剥离

java -jar mockwire.jar --port [port_number]
Run Code Online (Sandbox Code Playgroud)

2)我是否需要使用MockWire API?我需要为我的用例制作课程吗?在我的例子中,请求将通过JUnit测试用例触发以进行模拟.

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

4)我的用例是否有更好/更容易的框架?我读到了Mockable,但它对3个团队成员和免费层的演示域有限制.

Tom*_*Tom 30

我是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 ServerBetamax都是WireMock的成熟替代品,但是AFAIK它们不提供任何更明确的SOAP支持.

  • @Tom 你在 StackOverflow 上的表现并不多,但我只想说 WireMock 是一个很棒的工具包 - 非常感谢! (4认同)

mar*_*ers 12

我参加这个派对已经晚了三年多了,但我花了一些时间来解决同样的问题所以我虽然值得将我的解决方案记录为一个答案,所以它可能会让别人省去手动处理SOAP有效负载的麻烦.刮.

我做了一个合理的研究试图为我的集成测试套件解决这个问题.尝试了各种各样的事情,包括CXF自定义生成的服务器,SOAP-UI,一个CGLIB影响的库,它替换了测试环境中的真实客户端.

我最终使用WireMock和自定义请求匹配器来处理所有SOAP-yness.

它的要点是一个类,它处理SOAP请求的解组和SOAP响应的编组,以便为​​测试只需要JAXB生成的对象的作者提供一个方便的包装器,而不必关心SOAP的细节.

回应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();
}
Run Code Online (Sandbox Code Playgroud)

请求解组

/**
 * 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;
}
Run Code Online (Sandbox Code Playgroud)

除此之外,还有一些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))));
}
Run Code Online (Sandbox Code Playgroud)

结果你最终得到了一个很好的精益测试.

SoapContext context = new SoapContext(...) // URIs, QName, Prefix, ect
context.stubOperation("createUser", CreateUser.class, (u) -> "myUser".equals(u.getUserName()), new CreateUserResponse());

soapClient.createUser("myUser");
Run Code Online (Sandbox Code Playgroud)