web-dev-qa-db-ja.com

SOAPのWebサービス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 APIを使用する必要がありますか?ユースケースに合わせてクラスを作成する必要がありますか?私の場合、リクエストはモック用のJUnitテストケースを介してトリガーされます。

3)単純なURLパターンマッチングを実現するにはどうすればよいですか?上記のように、単純なモックが必要です。つまり、リクエストが http:// localhost:9001/endpoint1 に対して行われたときに応答を取得するだけです。

4)ユースケースにより良い/簡単なフレームワークはありますか? Mockableについて読みましたが、3つのチームメンバーと無料利用枠のデモドメインに制限があります。

11
Anurag

私はWireMockのクリエイターです。

WireMockを使用して、ごく最近クライアントプロジェクトのSOAPインターフェイスのコレクションをモックしたので、それが可能であることを証明できます。 SOAP UIよりも良いか悪いかについては、明確な利点がありますが、いくつかのトレードオフがあります。主な利点は、展開とプログラムによるアクセス/構成の相対的な容易さ、およびHTTPSや低レベルの障害挿入などのサポートです。ただし、SOAPペイロードを解析および生成するには、もう少し作業が必要です-SOAP UIのように、WSDLからコード/スタブを生成しません。

私の経験では、SOAP UIのようなツールを使用すると、より早く開始できますが、テストスイートが些細なものを超えて大きくなると、長期的にはメンテナンスコストが高くなる傾向があります。

ポイントに順番に対処するには:1)モックをどこかのサーバーで実行したい場合、これを行う最も簡単な方法は、説明したようにスタンドアロンJARを実行することです。コンテナにデプロイしようとすることをお勧めします-このオプションは、代替手段がない場合にのみ存在します。

ただし、統合テストまたは完全に自己完結型の機能テストのみを実行する場合は、JUnitルールを使用することをお勧めします。 a)デプロイされた他のシステムをプラグインする場合、またはb)非JVM言語から使用している場合、専用プロセスで実行することをお勧めします。

2)3つの方法のいずれかで構成する必要があります1)Java AP​​I、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 Server および Betamax はどちらもWireMockの成熟した代替手段ですが、知る限り、明示的なSOAPサポートは提供していません。

28
Tom

私はこのパーティーに3年以上遅れていますが、同じ問題を解決するのに時間がかかりましたので、解決策を答えとして文書化する価値があるので、他の誰かが手動で対処する頭痛を軽減するかもしれませんSOAPゼロからのペイロード。

私は統合テストスイートでこの問題を解決しようとする研究について合理的なことをしました。 CXFカスタム生成サーバー、SOAP-UI、テストコンテキストで実際のクライアントを置き換えるCGLIBの影響を受けたライブラリなど、あらゆる種類のものを試しました。

最終的にはWireMockを カスタムリクエストマッチャー で使用して、すべてのSOAP- ynessを処理しました。

その要点は、JAXBで生成されたオブジェクトのみを必要とするテスト作成者に便利なラッパーを提供するために、SOAPリクエストのアンマーシャリングとSOAPレスポンスのマーシャリングを処理するクラスでした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処理は、動作するための最も厄介な部分でした。そこから、WireMocksを補完する独自のAPIを作成するだけです。例えば

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");
11
markdsievers
  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>

0
Jin