Friday, June 20, 2008

Creating Mock Web Services in .Net

So, a situation arises where your code needs to make a Web Services call out to a different system. Chances are you'll go through the usual stages of adding a Web Reference to your project in Visual Studio, and then use the generated code to make your call. Simple, right?

Well, yes, and Visual Studio goes out of its way to simplify the creation of this client code, so you can get on with calling the service rather than concern yourself with the plumbing that's required.

But what if the service you are calling charges you for every call you make? What if you can't guarantee that you will be online during the development of your code? What if that service is currently under development and you don't know whether it will be available when you are doing your testing?

If any of these apply you need to remove the external call to the web service from your code. In a unit test scenario you would probably use something like NMock and architect your code to use a dependency injection pattern: injecting an NMock created object that matched an expected interface.

In other cases what you can do is create a mock Web Service. One that matches the interface of the live one, but which you control.

You can easily create interfaces that match a WSDL file by using the WSDL.exe program. I have a sneaky suspicion that this tool is used to create the client code from Visual Studio when you add a new Web Reference, but it can also perform the other way around.

First, obtain the WSDL of the Web Service you want to mock up. (If you are calling a .Net Web Service, then the path will probably end .asmx. To get the WSDL, just append ?wsdl to the path and you'll get the WSDL XML). Save this to your local machine.

Open up the Visual Studio Command Prompt and type wsdl.exe. You should get a heap of text explaining the command line switches, but if you don't your paths aren't mapped correctly. The wsdl.exe file should be somewhere on your machine though :)

Once you've found the tool, type

wsdl /language:CS /namespace:Your.Namespace.Here /out:Directory\To\Save\To\ /protocol:SOAP /serverinterface finally-your-wsdl-file-here.wsdl

There are other options too if you need them, like setting proxy username and passwords, but the one shown is what I've used and it works fine.

This will create a .cs file in the /out directory that contains a number of classes that match the object definitions in the WSDL. It will also contain one interface that you must implement in order to complete your mock Web Service.

To do this, create a new ASP.Net Web Application. (I guess you could create a Web Site, but I've not tried that as I don't like them). Add the code file you generated above to the project, then add a new Web Service.

In the code behind for this Web Service, change the class definition so that it implements the interface in the generated code. Visual Studio should help you out by generating method stubs so the class matches the interface.

And there you have it, place code in the method stubs to do what you want.

You now have your mock web service. Run the application to make sure that it works, and then make a note of the address of the new mock service. Enter this as the URL of the web service you want to call in your App/Web.config file in place of the live one, and your application should now call your mock instead of the live one.

A little tip though. I've noticed that (at least Visual Studio 2008) doesn't like attaching to 2 IIS processes for debugging. If you are calling your mock service from an ASP.Net application and you want to debug both, you'll need to start one of the applications up in the Visual Studio Development Server, instead of running under IIS. If you do this, make sure you assign an explicit port value rather than an auto generated one, otherwise your calling code won't be able to call it :)

2 comments:

Anonymous said...

In the words of Paul Daniels 'That's magic!'. I'm not sure you realise what a 'monster' you've unleashed when it comes to Mocking. I keep staring at classes we're modifying and cursing missing interface definitions, hands raised to the sky screaming 'But I want to use NMock dammit!'
Ah well, I'm adding proper structure where I find it lacking to allow us to break that dependency on 'other world' entities. Thank you just seems so inadequate :)

Editor said...

Same concept I have used with a mocking API called Mockito in Java, see my blog post Mocking .NET Web Services with Mockito.