web-dev-qa-db-fra.com

Utilisation de WireMock avec SOAP Services Web dans Java

Je suis totalement nouveau pour WireMock .

Jusqu'à présent, j'utilisais des réponses simulées à l'aide de SOAPUI. Mon cas d'utilisation est simple:

Il suffit de tirer SOAP requêtes XML vers différents points de terminaison ( http: // localhost: 9001/endpoint1 ) et de récupérer la réponse XML en conserve. Mais MockWrire doit être déployé en tant que service autonome sur un serveur dédié qui agira comme un emplacement central à partir duquel les fausses réponses seront servies.

Je voulais juste quelques suggestions de départ. Comme je peux le voir, WireMock est plus adapté aux services Web REST. Mes doutes sont donc les suivants:

1) Dois-je le déployer sur un serveur ou conteneur Web Java pour agir comme un service autonome toujours en cours d'exécution. J'ai lu que vous pouvez simplement

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

2) Dois-je utiliser des API MockWire? Dois-je créer des classes pour mon cas d'utilisation? Dans mon cas, les demandes seront déclenchées via des cas de test JUnit pour se moquer.

3) Comment puis-je obtenir une correspondance de modèle d'URL simple? Comme indiqué ci-dessus, j'ai juste besoin d'une moquerie simple, c'est-à-dire obtenir une réponse lorsque la demande est adressée à http: // localhost: 9001/endpoint1

4) Existe-t-il un cadre meilleur/plus facile pour mon cas d'utilisation? J'ai lu sur Mockable mais il a des restrictions pour 3 membres de l'équipe et un domaine de démonstration en niveau gratuit.

11
Anurag

Je suis le créateur de WireMock.

J'ai utilisé WireMock pour simuler une collection d'interfaces SOAP sur un projet client assez récemment, donc je peux attester que c'est possible. Quant à savoir si c'est mieux ou pire que SOAP UI, je dirais qu'il y a des avantages certains, mais avec des compromis. Un avantage majeur est la facilité relative de déploiement et d'accès/configuration programmatique, et la prise en charge de choses comme HTTPS et l'injection de pannes de bas niveau. Cependant , vous devez faire un peu plus de travail pour analyser et générer SOAP charges utiles - il ne fera pas de génération de code/stub à partir de WSDL comme SOAP UI le fera).

D'après mon expérience, des outils comme SOAP UI vous permettront de démarrer plus rapidement, mais ont tendance à entraîner des coûts de maintenance plus élevés à long terme lorsque votre suite de tests se développe au-delà de trivial.

Pour aborder vos points tour à tour: 1) Si vous souhaitez que vos simulateurs s'exécutent sur un serveur quelque part, la façon la plus simple de le faire est d'exécuter le JAR autonome comme vous l'avez décrit. Je déconseille d'essayer de le déployer dans un conteneur - cette option n'existe vraiment que lorsqu'il n'y a pas d'alternative.

Cependant, si vous souhaitez simplement exécuter des tests d'intégration ou des tests fonctionnels totalement autonomes, je vous suggère d'utiliser la règle JUnit. Je dirais que ce n'est qu'une bonne idée de l'exécuter dans un processus dédié si a) vous y branchez d'autres systèmes déployés, ou b) vous l'utilisez à partir d'un langage non JVM.

2) Vous devez le configurer de l'une des 3 façons suivantes: 1) les fichiers Java API, 2) JSON sur HTTP ou 3) les fichiers JSON. 3) est probablement le plus proche de ce à quoi vous êtes habitué avec SOAP UI.

3) Voir http://wiremock.org/stubbing.html pour de nombreux exemples de stubbing utilisant à la fois JSON et Java. Étant donné que SOAP a tendance à se lier aux URL de point de terminaison fixes, vous voulez probablement urlEqualTo(...). Quand j'ai tronqué SOAP dans le passé, je ' J'ai eu tendance à faire correspondre XML sur tout le corps de la demande (voir http://wiremock.org/stubbing.html#xml-body-matching ). Je suggère d'investir dans l'écriture de quelques-uns Java constructeurs pour émettre le corps de requête et de réponse XML dont vous avez besoin.

4) Mock Server et Betamax sont tous deux des alternatives matures à WireMock, mais AFAIK ils n'offrent pas de support plus explicite SOAP.

28
Tom

J'ai plus de trois ans de retard pour cette fête, mais il m'a fallu un certain temps pour résoudre le même problème, donc je pensais que cela valait la peine de documenter ma solution comme une réponse afin que cela puisse éviter à quelqu'un d'autre le mal de tête de traiter manuellement SOAP charges utiles à partir de zéro.

J'ai fait une recherche raisonnable sur la résolution de ce problème pour ma suite de tests d'intégration. J'ai essayé toutes sortes de choses, y compris les serveurs générés sur mesure CXF, SOAP-UI, une bibliothèque influencée par CGLIB qui remplace le vrai client dans un contexte de test.

J'ai fini par utiliser WireMock avec égaliseurs de demande personnalisés pour gérer toutes les SOAP- yness.

L'essentiel était une classe qui a géré le démasquage de SOAP requêtes et le marshaling de SOAP réponses afin de fournir un wrapper pratique pour tester les auteurs qui ne nécessitaient que JAXB généré des objets et n'a jamais eu à se préoccuper des détails de SOAP.

Marshaling de réponse

/**
 * 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();
}

Demande de démêlage

/**
 * 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;
}

En plus de cela, il y avait quelques utilitaires XPath pour jeter un coup d'œil dans la charge utile de la demande et regarder quelle opération était demandée.

Toutes les manipulations SOAP ont été les plus difficiles à faire fonctionner. À partir de là, il suffit de créer votre propre API pour compléter WireMocks. Par exemple

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))));
}

et en conséquence vous vous retrouvez avec un Nice, des tests maigres.

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

soapClient.createUser("myUser");
11
markdsievers
  1. J'exécute le serveur wiremock de façon autonome
  2. Je crée un fichier mapping.json, place dans mon dossier 'mappings' du faux projet

    {"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. Je crée un fichier xml de réponse, le place dans le dossier '__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>

0
Jin