如何模拟Web服务

时间:2009-10-23 18:14:36

标签: c# .net web-services moq mocking

我是否必须重写我的代码才能在界面中执行此操作?或者有更简单的方法吗?我正在使用Moq

4 个答案:

答案 0 :(得分:39)

我通常做的是围绕我的Web服务构建一个包装器或适配器,然后嘲笑它。

例如:

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

然后我只是存根服务适配器。

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

答案 1 :(得分:34)

最近写了一些关于单元测试和嘲笑的回复。我在其他地方写过,问问自己你正在测试的究竟是很重要的。关于你的具体情况,我希望答案是“我正在测试我的WebService正在暴露的业务逻辑”,而不是“我正在测试我的WebService” - 这是有区别的。


如果您的问题是服务器端

您通常不需要测试WebServices。 MS已经做到了。数百万人已经做到了。测试传输层,协议,WebServices的定义是浪费时间。

您需要定位业务逻辑。执行此操作的最佳方法是将您的业务逻辑与WebService分开。请考虑以下

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 ();
    }
}

没有直接调用WebService就无法测试该逻辑。你真正想要的是

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 ();
    }
}

在测试中

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

管理依赖项[如AuthenticationService成员]是一个单独的问题。但是,将WebMethods 简单直通转换为正确的底层业务类并完全从中删除逻辑,允许您定位“真实”用户代码,而不是典型WebService实现的管道。


如果您的问题是客户端

您有一个业务组件调用一个Web服务,我同意您不想为单元测试创​​建客户端。

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

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

在这里,您有依赖性问题,即无法在不实例化和托管WebService的情况下测试MyClient.CallService。如果您不拥有或托管所述远程服务,尤其令人不安。在这种情况下,是的,你应该写一个接口 - 再次分离和隔离业务逻辑。

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 ();
    }
}

在测试中

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

通常,如果类的依赖项是进程内服务,那么应用依赖注入[DI]或控制反转[IoC]等模式的决定取决于您 - 以及您希望隔离和单元测试这些服务将告知您的设计。但是,如果类的依赖项跨越进程边界,例如数据库或WebService,我强烈建议像上面那样应用这些模式。

真的,这只是简单的旧界面开发。你可能已经看到它是如何得到回报的。

:)

答案 2 :(得分:10)

很久以前我blogged about this。基本上使用部分类和一些努力(自动或手动,取决于您将更改Web服务的频率),您可以使Web服务代理类实现一个接口。然后你可以正常模拟它。

答案 3 :(得分:2)

有一种简单的方法。 例如,如果我们的WebService类名为DbService, 首先为它创建一个接口(例如IService),然后使用这个接口进行模拟,然后在项目中添加一个类并放入:

public partial class DbService:IService {

}

将类留空,因为Web服务是部分类我们使用此实现。 (以前