简体   繁体   English

.NET 中的单元测试服务层

[英]Unit Testing Service Layer in .NET

I am developing a new MVC 5 Application.我正在开发一个新的 MVC 5 应用程序。 I have an Integration Layer in my project where I have a service reference to an external web service.我的项目中有一个集成层,其中有一个对外部 Web 服务的服务引用。

I then created a interface that has some methods - I then have a service class which looks something like below:然后我创建了一个具有一些方法的接口 - 然后我有一个服务类,如下所示:

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

Note this is stripped down and in reality will add try catch block with Exception Handling, etc请注意,这是精简的,实际上将添加带有异常处理等的 try catch 块

I have a Integration.Tests project for Unit Testing of the integration layer.我有一个 Integration.Tests 项目,用于集成层的单元测试。 What is the best practice way of testing the GetName method.测试 GetName 方法的最佳实践方法是什么。 Should I add another Service Reference to the endpoint of the service in the Tests project or if we are using Moq can I create a instance of my actual service that the Web Layer for example will call - and how would that be done?我是否应该向测试项目中的服务端点添加另一个服务引用,或者如果我们使用的是 Moq,我是否可以创建我的实际服务的实例,例如 Web 层将调用该实例 - 这将如何完成?

The best practice for your situation lies in your architecture design.适合您情况的最佳实践在于您的架构设计。

Your service obviously depends on ExternalServiceClient , and you're intantiating it inside of your MyService class, which doesn't allow you to switch the dependency easily, causing you headaches when it's time to test it.您的服务显然依赖于ExternalServiceClient ,并且您在MyService类中对其进行了初始化,这不允许您轻松切换依赖项,导致您在测试它时头疼。

The real question should be:真正的问题应该是:

How may I design my service in order to make it easily testable?我该如何设计我的服务以使其易于测试?

And the answer isDependency Injection .答案是Dependency Injection

Because you will be able to mock MyService dependency, you will be able to thoroughly test it and be able to certify, by red-green refactoring, that your service works flawlessly.因为您将能够模拟MyService依赖项,所以您将能够对其进行彻底测试,并能够通过红绿重构来证明您的服务可以完美运行。

In my humble opinion, your class should like this:以我的拙见,你的班级应该是这样的:

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

This way, you will be able to replace your dependency at your will, hence the use of a mock.这样,您就可以随意替换您的依赖项,因此可以使用模拟。

Using NUnit and Moq , you could test your service as follows:使用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;
}

In general, it is good practice (as you have readonly semantic for ExternalServiceClient ) to have parametrized constructor to be able to inject the dependency on object construction.通常,使用参数化构造函数来注入对对象构造的依赖是一种很好的做法(因为您对ExternalServiceClient具有只读语义)。 Then you can inject the mock in your test case.然后你可以在你的测试用例中注入模拟。

In your case, if there is interface(s) that implemented in ExternalServiceClient在您的情况下,如果有在ExternalServiceClient中实现的接口

private readonly ExternalServiceClient _serviceClient;

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

Then use it as:然后将其用作:

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

and in test并在测试中

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

If there is no implemented interfaces and ability to add it (since it is external), you must do some tricks with virtuallity.如果没有实现的接口和添加它的能力(因为它是外部的),你必须用虚拟性做一些技巧。

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

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