繁体   English   中英

.NET 中的单元测试服务层

[英]Unit Testing Service Layer in .NET

我正在开发一个新的 MVC 5 应用程序。 我的项目中有一个集成层,其中有一个对外部 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 层将调用该实例 - 这将如何完成?

适合您情况的最佳实践在于您的架构设计。

您的服务显然依赖于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;
}

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

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

[TestFixture]
public class MyServiceTests {
    [TestFixture]
    public class GetCoordinates : MyServiceTests {
        // Given
        string expected = "Name";
        Coordinate[] coordinates = new Coordinate[] { ... }
        externalServiceClientMock.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() {
        externalServiceClientMock = new Mock<ExternalServiceClient>("WSHttpBinding_IMyService");
        myService = new MyService(externalServiceClientMock.Object);
    }
    
    private Mock<ExternalServiceClient> externalServiceClientMock;
    private MyService myService;
}

通常,使用参数化构造函数来注入对对象构造的依赖是一种很好的做法(因为您对ExternalServiceClient具有只读语义)。 然后你可以在你的测试用例中注入模拟。

在您的情况下,如果有在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);

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM