Использование WireMock с веб-службами SOAP в Java

Я совершенно новичок в WireMock.

до сих пор я использовал макет ответов, используя SOAPUI. Мой случай использования прост:

просто запуск SOAP XML-запросов к различным конечным точкам (http://localhost:9001/endpoint1) и получение консервированного ответа XML. Но MockWrire должен быть развернут как автономная служба на выделенном сервере, который будет действовать в центральном месте, откуда будут подаваться фиктивные ответы.

просто хотел некоторые начальные предложения. Как я вижу, WireMock больше подходит для веб-сервисов REST. Поэтому мои сомнения таковы:

1) мне нужно развернуть его на веб-сервере или контейнере java, чтобы действовать как всегда с автономной службой. Я читал, что вы можете просто отключиться, используя

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

2)Нужно ли использовать API MockWire? Мне нужно сделать классы для моего прецедента? В моем случае запросы будут запускаться через тестовые случаи JUnit для насмешек.

3) Как мне добиться простой шаблон URL-адреса? Как указано выше, мне просто нужно просто издеваться над i.e получить ответ при запросе http://localhost:9001/endpoint1

4) Есть ли лучшая/более простая структура для моего usecase? Я читал о Mockable, но у него есть ограничения для 3 членов команды и демо-домена в свободном уровне.

3 ответов


Я создатель WireMock.

я использовал WireMock для макета коллекции интерфейсов SOAP в клиентском проекте совсем недавно, поэтому я могу подтвердить, что это возможно. Что касается того, лучше или хуже, чем SOAP UI, я бы сказал, что есть определенные недостатки, но с некоторыми компромиссами. Основным преимуществом является относительная простота развертывания и программного доступа / конфигурации, а также поддержка таких вещей, как HTTPS и низкоуровневая инъекция ошибок. Тем не менее, вам нужно сделать немного больше работы, чтобы анализировать и генерировать полезные нагрузки SOAP - он не будет делать генерацию кода/заглушки из WSDL, как SOAP UI.

мой опыт заключается в том, что такие инструменты, как SOAP UI, позволят вам начать работу быстрее, но, как правило, приводят к более высоким расходам на обслуживание в долгосрочной перспективе, когда ваш набор тестов выходит за рамки тривиального.

чтобы обратиться к вашим точкам в свою очередь: 1) Если вы хотите, чтобы ваши насмешки запускались на сервере где-то, самый простой способ сделать это-запустить автономную банку, как вы описали. Я бы не советовал пробовать чтобы развернуть его в контейнер-этот параметр действительно существует только тогда, когда нет альтернативы.

однако, если вы просто хотите запустить интеграционные тесты или полностью автономные функциональные тесты, я бы предложил использовать правило JUnit. Я бы сказал, что это только хорошая идея запустить его в выделенном процессе, если a) вы подключаете к нему другие развернутые системы или b) вы используете его с языка, отличного от JVM.

2) вам нужно настроить его одним из 3 способов 1) API Java, 2) JSON через HTTP или 3) файлы JSON. 3) вероятно, ближе всего к тому, к чему вы привыкли с SOAP UI.

3) см.http://wiremock.org/stubbing.html для множества примеров stubbing, использующих как JSON, так и Java. Поскольку SOAP имеет тенденцию привязываться к фиксированным URL-адресам конечных точек, вы, вероятно, хотите urlEqualTo(...). Когда я тушил мыло в прошлом, я имел тенденцию к совпадению XML по всему телу запроса (см. http://wiremock.org/stubbing.html#xml-body-matching). Я бы предложил инвестировать в написание нескольких Java-построителей для создания необходимого XML-кода тела запроса и ответа.

4) Макет Сервера и Бетамакс являются зрелыми альтернативами WireMock, но AFAIK они не предлагают более явной поддержки SOAP.


Я опаздываю на три года на эту вечеринку, но мне потребовалось некоторое время, чтобы проработать ту же проблему, поэтому я подумал, что это достойно документирования моего решения в качестве ответа, чтобы это могло спасти кого-то еще от головной боли вручную иметь дело с полезными нагрузками мыла с нуля.

Я сделал разумное исследование, пытаясь решить эту проблему для моего набора интеграционных тестов. Пробовал все виды вещей, включая пользовательские серверы CXF, SOAP-UI, библиотеку CGLIB, которая заменяет реальный клиент в тестовом контексте.

Я закончил использование WireMock с пользовательские сопоставления запросов для обработки всех SOAP-yness.

суть его была классом, который обрабатывал unmarshaling запросов 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();
}

Запрос 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;
}

в дополнение к этому были некоторые утилиты XPath для заглядывания в полезную нагрузку запроса и просмотра запрошенной операции.

все обработки мыла был fiddliest части, чтобы получить работу. Оттуда он просто создает свой собственный 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. Я создаю отображение.файл json, поместите в папку "сопоставления" моего макета проекта

    {"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>