模拟类中定义的对象

时间:2017-11-02 17:27:12

标签: c# unit-testing moq

使用以下代码:

class Client {
    private Service _service;

    public Client() {
        _service = new Service; // Connection is made to endpoint
    }

    public string GetData() {
        return _service.ReadData();
    }
}

如何在不修改构造函数或访问修饰符的情况下使用Moq来模拟服务?

2 个答案:

答案 0 :(得分:3)

为了实现这一目标(或至少,干净利落),您必须执行依赖注入。您的类不应该实例化服务。它应该接收已经实例化的服务实例。

class Client {
    private Service _service;

    public Client(Service service) {
        _service = service; // maybe you check for null or other checks here
    }

    public string GetData() {
        return _service.ReadData();
    }
}

下一步:您应该将您的类设置为依赖于接口 IService而不是实际的Service类。这样,您可以轻松创建另一个可以在实例化类时注入而不是服务的ServiceMock。

class Client {
    private IService _service;

    public Client(IService service) {
        _service = service; // maybe you check for null or other checks here
    }

    public string GetData() {
        return _service.ReadData();
    }
}

与您的要求相关:

  

不修改构造函数或访问修饰符?

这不是一个好习惯,它可能变得很脏。我没有这样的解决方案,因为你使用直接类型而不是接口。

我不知道你是在处理一些遗留代码还是第三方库,但是通过编程接口而不是类来预测这类问题是关键。

答案 1 :(得分:1)

再次强调这一点:

  • 如果您的new Service()构造函数正在连接到端点,则您将无法模拟此代码。即使您之后更换了实例,UnitTests也不应该依赖于另一个可用的端点。
  • 如果Service未提供可模拟的接口IService且其方法不是虚拟的,则根本无法模拟它。您需要创建一个包装器接口和实现

一个常见的解决方法是创建一个具有受限可见性(例如内部)的第二个构造函数,并使用它将模拟注入到您的类中。您可以使用InternalsVisibleTo属性控制可见性。有一些关于为测试创建构造函数的讨论,但这是朝着正确方向迈出的第一步。

class Client {
    private Service _service;

    // Only for UnitTests
    internal Client(Service service) {
        _Service = service
    }

    public Client() {
        _service = new Service(); // Connection is made to endpoint
    }

    public string GetData() {
        return _service.ReadData();
    }
}

将所有评论中的内容整合到一个可用的示例中:

class Client {
    private IService _service;

    Client(IService service) {
        _service = service;
    }

    public string GetData() {
        return _service.ReadData();
    }
}

class ClientFactory {

    public Client CreateClient(){
        var service = new Service(); // Connection is made to endpoint
        return new Client(service);
    }
}