I've been working on a solution lately that calls an existing WebService as part of the business functionality it implements. Obviously, I needed a good way of testing it, and I wanted to mention here a pattern I've used with good success for that.
The overall pattern I'm using now is a combination of several patterns, in particular Service Gateway and a sub-Service Interface (I'll explain this in a moment), coupled with the use of Mock Objects to support unit testing. In my case, I've been using lately Ayende's Rhino Mocks library, which I've found excellent overall.
The Service Interface
The first thing I do to make testing the code calling the service easier is to clearly isolate the business code from the WebService call. This doesn't need to be a huge isolation necessarily (it depends on your needs), but it does mean adding one level of indirection between the calling code and your WebService proxy class. Here's where the patterns help a lot:
First, I define an interface for accessing the WebService, which may (or may not) match the actual interface of the service as exposed by the client-side proxy class. One reason to make it different might be if you only need a few of the operations exposed and not all of them. For example, if your service exposes a dozen of operations and you only need three of them, don't make your life harder by having to define and implement an interface more complex than it needs to be; keep it simple. This is why I call this a "Sub-Service Interface", since it isn't an straight Service Interface pattern implementation.
If you are using WCF to access the service, then by all means using the ServiceContract interface you get from WCF might be an acceptable compromise.
Having that interface is key to enabling the testability we want. With that interface in place, you can actually begin unit-testing your code right away (even before doing the next obvious step), by using Mock Objects to mock your new service. To make it easier to test, you'll also want to use an Inversion of Control pattern to hook the classes implementing your business logic with the implementation of your service interface. In my code, I use the Windsor container from the Castle Project for this.
Some things to keep in mind during your testing: You'll probably want to test the business code under several different situations, including simulating both when the service works correcty as well as when it fails. For example, in our current project there are several paths the code can follow when the service fails depending on what error it returns, and we want to be able to cover them all in our Unit tests.
The Service Gateway
Now that we have a service interface defined for the business logic to access our WebService, and we've tested the business code, we want to implement the code that actually calls the WebService. Obviously, much of that code is the proxy class generated by Visual Studio (or whatever tool you're using to generate it like WSCF, which we also use), but since we're adding a new level of indirection, we need to create a class that will implement our Sub-Service interface and will thus wrap the calls to the proxy class: our Service Gateway.
In many cases, the Service Gateway will be trivial, and might just be a pass-through to the proxy class methods. Oftentimes, however, this will be a great place to centralize logic associated with the WebService call, like configuration and error handling.
For example, if the service you're calling implements WS-Security, the service gateway is a great place to handle the steps necessary to associate the correct security credentials to your service proxy class. For example, in our current project, we use WSE UsernameTokens to access the WebService. As it happens, the username and password to use to connect to the webservice cannot be embedded in any configuration file; instead they are stored in an external configuration system our client uses. Querying the external configuration system for the credentials information, creating the appropriate UsernameToken and associating it with the requests is done at the Service Gateway class, keeping the rest of the code clean and independent of these considerations.
Another example of why this is good: In our project, if the service call fails because of network-related issues, like a connection timeout or a connection refused, we're required to send a proactive alert about the problem; we can easily do this in a centralized fashion from our Service Gateway.
One interesting point here is that in our project, the Service Gateway class does not do data conversions, though in general I would recommend you do it. Some reasons we did otherwise this time were:
- Our interface is pretty simple, and there isn't really any complex data to transform
- The few transformations that are required are not uniform. In other words, not every place where a given operation is called is the transformation required.
- Those few simple transformations there are, we implemented in their own separate classes which were independently unit-tested, so hooking them into the business logic at the correct place was low-risk and because of (2) it actually meant less code we needed to write and test (i.e. a good thing).
- We have some error mapping transformations were we convert some of the error codes returned by the external WebService to a different representation. However, those error transformations again were not uniform, so trying to centralize them would actually mean more and more convoluted code.