Wiremock: SOAP support

Created on 16 Aug 2014  路  17Comments  路  Source: tomakehurst/wiremock

Hello,

I was wondering if wiremock supports https SOAP web services. The examples all seem to me to indicate a path needs to be unique. In my case every request is a POST to the same end point like:

https://myserver.com:443/sdk

Where the payloads being sent would be unique, and the responses coming back would be unique. If this is possible would you mind showing me a simple example?

Thanks

Most helpful comment

That's how I configured it to work with Soap calls.

/mappings/SayHello.json

{
  "request": {
    "method": "POST",
    "url": "/mocked-soap-service.wsdl",
    "headers" : {
      "SOAPAction" : {
        "contains" : "#SayHello"
      }
    }
  },
  "response": {
    "status": 200,
    "bodyFileName": "SayHello.xml",
    "headers": {
      "Content-Type": "application/xml"
    }
  }
}

/files/SayHello.xml

<?xml version="1.0"?>

<soap:Envelope
        xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
        soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
    ...
    Message information goes here
    ...
</soap:Envelope>

Executing request in PHP

<?php
$params = array(
  'location' => 'http://localhost:8080/mocked-soap-service.wsdl',
  'uri' => 'http://localhost:8080/mocked-soap-service.wsdl',
);

$client = new SoapClient(null, $params);
var_dump($client->__soapCall("SayHello", array("SomeKindOfID" => 1234)));

All 17 comments

In principle it's possible to mock SOAP responses, using request body matching (full XML or XPath) to differentiate.

e.g.

stubFor(post(urlEqualTo("/sdk"))
    .withRequestBody(matchingXPath("/packages[count(package) = 3]"))
    .willReturn(aResponse()
        .withStatus(200)
        .withBody("<soap ....>"));

How about SOAP support for the standalone version? Is this possible?

Nothing to stop you using it for SOAP right now. I did on a project a few months ago.

Problems I am facing are:

1) Wiremock in record mode records only the definitions. I need the actual responses recorded as well.
2) Using SOAP a given url may house quite a few different operations. Wiremock standalone is meant to cache only one response per given url.

Can you give me some guidelines on how to solve these two? Am I missing something or is this impossible to do with wiremock standalone?

1) You should see the response bodies being recorded under the __files directory. Is this not the case? Or is something else missing?
2) WireMock can match on body content and headers in addition to URL, so if your mappings specify a SOAPAction header and XML body you'll get different responses for different requests.

1) You should see the response bodies being recorded under the __files directory. Is this not the case? Or is something else missing?

It is there, but from 5 calls being made, only 1 is being recorded and it is not the actual call with the data and all that - it's just the WSDL definition.

2) WireMock can match on body content and headers in addition to URL, so if your mappings specify a SOAPAction header and XML body you'll get different responses for different requests.

In general, the SOAP protocol does not pass the action as a header - it's in the body of the request in XML format. Will have a look at using custom header. Feels like a workaround, but should do the job.

The header isn't necessary, you should be able to match on body and url alone.

The recorder is a bit simplistic in terms of how it figures out whether to treat a request as already arrived, so that's probably why you're seeing only one recording. Unfortunately this means you'll need to hand craft the additional variations (or code them).

That's how I configured it to work with Soap calls.

/mappings/SayHello.json

{
  "request": {
    "method": "POST",
    "url": "/mocked-soap-service.wsdl",
    "headers" : {
      "SOAPAction" : {
        "contains" : "#SayHello"
      }
    }
  },
  "response": {
    "status": 200,
    "bodyFileName": "SayHello.xml",
    "headers": {
      "Content-Type": "application/xml"
    }
  }
}

/files/SayHello.xml

<?xml version="1.0"?>

<soap:Envelope
        xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
        soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
    ...
    Message information goes here
    ...
</soap:Envelope>

Executing request in PHP

<?php
$params = array(
  'location' => 'http://localhost:8080/mocked-soap-service.wsdl',
  'uri' => 'http://localhost:8080/mocked-soap-service.wsdl',
);

$client = new SoapClient(null, $params);
var_dump($client->__soapCall("SayHello", array("SomeKindOfID" => 1234)));

@tomakehurst, I still think the recorder would benefit getting improved. It would spare the hassle of creating the content manually. Could you please add that in the roadmap?

Can you be a bit more specific about what you're after?

SOAP support is on the roadmap (although admittedly not in my badly organised issues list), but there's quite a lot of different things that could entail.

Almost 8 months later but here I go.. just started using WireMock with SOAP web services and, due to the recorders "simplicity" to differentiate requests (as pointed by @tomakehurst), I just kept repeating:

  1. Run WireMock with proxy-all and record-mappings enabled
  2. Make a request to WireMock using Postman
  3. Adjust bodyPatterns to use contains instead of matching the whole XML
  4. Rename the mappings/.json file to .bkp
  5. Repeat

After calling all my endpoints I just renamed .bkp files to .json, ran WireMock one more time and everything was working as expected. This isn't a superb solution but with a limited number os endpoints, helps (and I think it's better than handcrafting all requests).

Now, I think the recorder could be improved implementing different strategies to differentiate requests. I can see 3 different forms right now:

  1. Body equality: that's how WireMock works today
  2. SOAPAction header: when the header is used, it's certainly the way to go. The recorder could create the equality conditions (and differentiate future requests, of course) based on it
  3. 's first element name: when there is no SOAPAction header, usually the first element of the body can be used to distinguish the request (ie: createUserRequest, cancelApplication). Same thing: the recorder could create equality conditions and differentiate requests based on it.

Does it make sense to you guys? I haven't take a look at the source code yet, but I believe it would be a nice addition to recording the never ending world of SOAP Web Services. :)

Have you tried using the new recorder? It's a lot more configurable than the original version and can construct scenarios from runs of identical requests.

Just read about it now (shame on me)! The "Repeats as scenarios" kills the need to rename/restart wiremock, indeed.

Anyway, I believe more generalist Request Body Matchers would be a great addition to SOAP services (the 3 different strategies I cited before). Matching the whole body makes sense in very little cases (at least in my experience!).

What u think? Worth opening another issue with this feature suggestion?

I have half an idea about this.

I agree that matching the whole body is often a bad idea - it can get very computationally expensive, and it ends up being brittle as the API changes.

I'd like to fix this by adding some additional record config with a collection of XPaths that should be used as body matchers.

For example, if I'm expecting to record two types request:

<user>
  <id>abd-efg</id>
</user>

and

<order>
  <orderNumber>567</orderNumber>
</order>

Then I can configure the recorder to find the ID elements:

"bodyXPaths": [
  "//id", "/order/orderNumber"
]

Which would produce mappings like:

{
  "request": {
    "bodyPatterns": [
      { "matchesXPath": "//id" }
    ]
  }
}

or

{
  "request": {
    "bodyPatterns": [
      { "matchesXPath": "/order/orderNumber" }
    ]
  }
}

Depending which request document it saw.

What I'm not sure how to handle is when more than one of your XPaths matches a given request.

Hmm.. yeah. That would solve the idea about matching based on body properties.

If more than one matcher is fired Wiremock should simply return the first (as it already does in scenarios cases, as far as I recall).

The last suggestion would be some to allow some kind of recorder config that would record specific headers and generate appropriate mappings to match that specific value (SOAPAction values, in this case).

Yeah, I guess in the simple case picking the first match would be fine.

It gets a little harder if there isn't a single field that's uniquely identifying e.g. you need to match on both first and last name. Perhaps the config model needs to account for that i.e. allowing you to specify (or imply) AND and OR.

Header specifications in the recorder are already implemented :-)

That's how I configured it to work with Soap calls.

/mappings/SayHello.json

{
  "request": {
    "method": "POST",
    "url": "/mocked-soap-service.wsdl",
    "headers" : {
      "SOAPAction" : {
        "contains" : "#SayHello"
      }
    }
  },
  "response": {
    "status": 200,
    "bodyFileName": "SayHello.xml",
    "headers": {
      "Content-Type": "application/xml"
    }
  }
}

/files/SayHello.xml

<?xml version="1.0"?>

<soap:Envelope
        xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
        soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
    ...
    Message information goes here
    ...
</soap:Envelope>

Executing request in PHP

<?php
$params = array(
  'location' => 'http://localhost:8080/mocked-soap-service.wsdl',
  'uri' => 'http://localhost:8080/mocked-soap-service.wsdl',
);

$client = new SoapClient(null, $params);
var_dump($client->__soapCall("SayHello", array("SomeKindOfID" => 1234)));

But then do you mean we need to have one url for one kind of response. If i need to give 5 different kinds of response, do i need to add 5 urls and have five files served like you explained above?

Was this page helpful?
0 / 5 - 0 ratings