开发者

How to mock a web service

Do I have to rewr开发者_开发知识库ite my code to do this into an interface? Or is there an easier way? I am using Moq


What I usually do is build a wrapper or an adapter around my web service and just mock that.

for instance:

public class ServiceAdapter: IServiceAdapter
{
    public void CallSomeWebMethod()
    {
        var someService = new MyWebService();
        someService.SomeWebMethod();
    }
}

Then I just stub the service adapter.

[Test]    
public void SomeMethod_Scenario_ExpectedResult()
{
    var adapterMock = new Mock<IServiceAdapter>();
    //do your test
}


been writing a couple of responses about unit testing and mocking lately. I wrote elsewhere that it's important to ask yourself what exactly are you testing. Regarding your particular situation, I would hope the answer is "I am testing the business logic my WebService is exposing", and not "I am testing my WebService" - there's a difference.


If your concerns are server-side

You do not need to test WebServices in general. MS has already done that. Millions of people have done that. Testing the transport layer, the protocol, the definition of WebServices is a waste of time.

You need to target your business logic. The best way to do this is to separate your business logic from your WebService. Consider the following

public class MyWebSevice : System.Web.Services.WebService
{
    private AuthenticationService _auth = new AuthenticationService ();
    private int _count = 0;
    [WebMethod]
    public string DoSomething ()
    {
        // embedded business logic, bad bad bad
        if (_auth.Authenticate ())
        {
            _count++;
        }
        return count.ToString ();
    }
}

there is no way to test that logic without invoking the WebService directly. What you really want is

public class MyService 
{
    // keeners will realise this too should be injected
    // as a dependency, but just cut and pasted to demonstrate
    // isolation
    private AuthenticationService _auth = new AuthenticationService ();
    private int _count = 0;
    public string DoSomething ()
    {
        if (_auth.Authenticate ())
        {
            _count++;
        }
        return count.ToString ();
    }
}

in prod

// this web service is now a consumer of a business class,
// no embedded logic, so does not require direct testing
public class MyWebSevice : System.Web.Services.WebService
{
    private readonly MyService _service = new MyService ();

    [WebMethod]
    public string DoSomething ()
    {
        _service.DoSomething ();
    }
}

in test

// test business logic without web service! yay!
[Test]
public void Test_DoSomething ()
{
    MyService service = new MyService ();
    string actual = service.DoSomething ();
    // verify results
}

managing dependencies [like the AuthenticationService member] is a separate issue. However, making your WebMethods simple passthroughs to proper underlying business classes and removing logic from them completely, allows you to target "real" user code as opposed to the plumbing of your typical WebService implementation.


If your concerns are client-side

You have a business component calling a webservice, and I agree that you don't want to create a client for unit testing.

public partial class MyWebService :
    System.Web.Services.Protocols.SoapHttpClientProtocol 
{
    ...
    public string DoSomething () { ... }
}

public class MyClient
{
    public void CallService ()
    {
        MyWebService client = new MyWebService ();
        client.DoSomething ();
    }
}

Here, you have dependency issues, namely you cannot test MyClient.CallService without instantiating and hosting your WebService. Especially disconcerting if you do not own or host said remote service. In this case, yes, you should write against an interface - once again to separate and isolate business logic.

public interface IMyWebService
{
    string DoSomething ();
}

public class MyWebServiceWrapper : IMyWebService
{
    public string DoSomething () 
    {
        MyWebService client = new MyWebService ();
        client.DoSomething ();
    }
}

public class MyClient
{
    private readonly IMyWebService _client = null;
    public MyClient () : this (new MyWebServiceWrapper ()) { }
    public MyClient (IMyWebService client)
    {
        _client = client;
    }
    public void CallService ()
    {
        _client.DoSomething ();
    }
}

in test

[Test]
public void Test_CallService ()
{
    IMyWebService mockService = null;
    // instantiate mock with expectations
    MyClient client = new MyClient (mockService);
    client.CallService ();
    // verify results
}

In general, if a class's dependencies are in-proc services, the decision to apply a pattern like Dependency Injection [DI] or Inversion of Control [IoC] is up to you - and your desire to isolate and unit test these services will inform your design. However, if a class's dependencies cross a process boundary, eg Database or WebService, I highly recommend applying these patterns as we did above.

Really, it's just plain old interface development. You probably already see how it pays off.

:)


I blogged about this a long time ago. Basically using partial classes and a bit of effort (either automated or manual, depending on how often you're going to change the web service) you can make the web service proxy class implement an interface. You can then mock it as normal.


there is an easy way. for example if we have WebService class with the name DbService, first create an interface for it (ex. IService), and use this interface for mocking, then add a class to your project and put this:

public partial class DbService:IService {

}

leave class empty, because of web services are partial class we use this implementation. (previously

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜