Testing Web Service Clients
As using Web Services becomes a fashionable trend, Test Driven Developers are asking: “How do I test my application that is a Web Service Client”? On the one hand, the testing approach is not different from working with Database, file system, or 3rd party API. On the other hand, we can use WS specifics to leverage testing. Let’s take a closer look.
Consider a typical application that, as part of domain logic implementation, consumes a web service. A DomainImp class calls DomainService – a service abstraction driven by domain logic. Think Evans, Domain Driven Design, Adapter, and Dependency Inversion Principle. Domain Service resolves domain specific call into one or several calls to the Web Service. Each is calling a Web Service Proxy class, transferring a SOAP* message over wire to the target machine, and executing WebService code there.
There are 4 seams. We can mock or stub any of 1) Domain Service, 2) WS Proxy, 3) Transport service 4) WebService.
1) Mock Domain Service. This is a TDD classic: extract an interface if you don’t have it yet, and substitute a production implementation by the mock for the test. This works the best for domain unit testing, especially if calling a WS is peripheral functionality, and/or only small subset of WS is used.
2) Mock Web Service Proxy. If the use of WS is central to the application logic and unlikely to change, and/or most of Web Service methods are used, you may find the DomainService redundant. Then mock Web Service Proxy class! This is simple: extract the interface, implement a mock/stub, switch domain code to use mock/stub if unit testing. A slightly different way is explained here. BTW, if you are with Visual Studio, I recommend to copy reference.cs, rename or “namespace” WS Proxy class, and work with it. If you don’t, Refresh Web Reference command may [and eventually will] mess things up.
3) Mock a service on a transport layer. The request and response travel over the wire. It calls for a simple plan to mock it up: sniff the response, and on request just shoot it back.
a) Sniff the request/response. I use tcpTrace or proxyTrace from PocketSoap.com.
b) Bring up the mock server to take requests and shoot back responses. How about writing a trivial socket server that listens on the port and bluntly spits out the hardcoded response? Then smart it up: base the response on the request. Or make it configurable. Or... Here you scream “too much!” and are about to give up. But hold on! there is a tool, and it is cool!! SoapUI: check it out, and go straight to Mocking Web Services. Here is how I used it:
- Import a WSDL (file or URL). It generates requests to play with the actual Web Service.
- Generate Mock Web Service. I can have all web methods, some of them, or any combinations from all WSDLs you have imported.
- Right from soapUI, test my Mock to see if it works.
- Stick responses captured at step a). I can have many responses, control their order, even script, were I groovy savvy :(
- Configure my client to point to the Mock Web Service, Run, have fun.
4) Build a mock Web Service. In .NET case, I run wsdl /si to generate ASP.NET Web Service Interface. Alternatively, check out WSCF - Web Services Contract First http://www.thinktecture.com/resourcearchive/tools-and-software/wscf. I didn’t get to try it myself: wsdl /si worked fine for me. Then I use Visual Studio Refactoring/Generate Implementation that builds me a stab. Then I implement the mock/stab pretty much like in case of WS Proxy.
Mocking a Web Service gives a lot of control. The trouble is to build complex types. I had to make breakpoints in WebProxy while working with real WebService, intercept the responses, interrogate their data, and manually re-create them on Web Service Mock.
The methods #1 and #2 requires code changes to build the test; they are good suit for unit testing. With #3 and #4 testing is done against full “release” build, which makes them suit for black box QA tests, performance, etc. In practice I prefer #1, Mock Domain Service, for unit tests, augmented with #3, Mock a service with a tool, for acceptance, QA tests and off-line demos.