.NET中的单元测试服务层

时间:2014-05-01 14:02:23

标签: c# unit-testing design-patterns moq

我正在开发一个新的MVC 5应用程序。我的项目中有一个Integration Layer,我有一个外部Web服务的服务引用。

然后我创建了一个有一些方法的接口 - 然后我有一个服务类,如下所示:

public class MyService : IMyService
{
    private readonly ExternalServiceClient _serviceClient;

    public MyService()
    {
        _serviceClient = new ExternalServiceClient ("WSHttpBinding_IMyService");
    }

    public string GetName(Coordinate[] coordinates)
    {
        string name = string.Empty;

        name = _serviceClient.GetInformationsForCoordinates(coordinates);

        return name;
    }

请注意,这将被删除,实际上会添加带有异常处理的try catch块等

我有一个Integration.Tests项目,用于集成层的单元测试。测试GetName方法的最佳实践方法是什么?我是否应该在测试项目中向服务的端点添加另一个服务引用,或者如果我们使用Moq,我可以创建一个实际服务的实例,例如Web层将调用它 - 以及如何完成?

2 个答案:

答案 0 :(得分:8)

您的情况的最佳做法取决于您的架构设计。

您的服务显然取决于ExternalServiceClient,并且您在MyService类内部对其进行了实例化,这不允许您轻松切换依赖关系,从而导致您头痛是时候测试一下了。

真正的问题应该是:

  

我如何设计我的服务以使其易于测试?

答案是Dependency Injection

因为你可以模仿MyService依赖,你将能够彻底测试它,并能够通过红绿重构证明你的服务完美无瑕。

以我的拙见,你的班级应该这样:

public class MyService : IMyService {
    public MyService(ExternalServiceClient serviceClient) {
        externalServiceClient = serviceclient;
    }

    public string GetName(Coordinate[] coordinates) {
        string name = string.Empty;
        name = externalServiceClient.GetInformationForCoordinates(coordinates);
        return name;
    }

    private readonly ExternalServiceClient externalServiceclient;
}

这样,您就可以随意替换您的依赖项,从而使用模拟。

使用NUnit Moq ,您可以按如下方式测试您的服务:

[TestFixture]
public class MyServiceTests {
    [TestFixture]
    public class GetCoordinates : MyServiceTests {
        // Given
        string expected = "Name";
        Coordinate[] coordinates = new Coordinate[] { ... }
        externalServiceClient.Setup(esc => esc.GetInformationForCoordinates(coordinates)).Returns(expected);

        // When
        string actual = myService.GetName(coordinates);

        // Then
        externalServiceClientMock.Verify(esc => esc.GetInformationCoordinates(coordinates));
        Assert.AreEqual(expected, actual);
    }

    [SetUp]
    public void MyServiceSetup() {
        externalServiceClient = new Mock<ExternalServiceClient>("WSHttpBinding_IMyService");
        myService = new MyService(externalServiceClientMock.Object);
    }

    private Mock<ExternalServiceClient> externalServiceClientMock;
    private MyService myService;
}

答案 1 :(得分:1)

一般情况下,将参数化构造函数注入对象构造的依赖性是一种很好的做法(因为ExternalServiceClient具有只读语义)。然后你可以在你的测试用例中注入mock。

在您的情况下,如果存在在ExternalServiceClient

中实施的界面
private readonly ExternalServiceClient _serviceClient;

public MyService(IExternalServiceClient serviceClient)
{
    _serviceClient = serviceClient;
}

然后将其用作:

var service = new MyService(new ExternalServiceClient ("WSHttpBinding_IMyService"));

并在测试中

IExternalServiceClient  mockObject = //construct mock with desired behabiour then pass to ctor
var service = new MyService(mockObject);

如果没有实现的接口和添加它的能力(因为它是外部的),你必须做一些虚拟的技巧。

相关问题