C# 模拟类中定义的对象

C# 模拟类中定义的对象,c#,unit-testing,moq,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

使用以下代码:

class Client {
    private Service _service;

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

    public string GetData() {
        return _service.ReadData();
    }
}
在不修改构造函数或访问修饰符的情况下,如何使用Moq模拟服务?

要做到这一点(或至少是干净地做到这一点),您必须进行依赖项注入。您的类不应实例化该服务。它应该接收一个已经实例化的服务实例

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();
    }
}
下一步:您应该将类设置为依赖接口而不是实际的服务类。通过这种方式,您可以轻松创建另一个ServiceMock,当您实例化类时,该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();
    }
}

与您的要求相关:

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

这不是一个好的做法,它可能会变得非常肮脏。我没有这样的解决方案,因为您使用的是直接类型而不是接口

我不知道您是否正在处理一些遗留代码或第三方库,但通过编程到接口而不是类来解决这类问题是这里的关键。

要做到这一点(或者至少是干净地做到这一点),您必须进行依赖项注入。您的类不应实例化该服务。它应该接收一个已经实例化的服务实例

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();
    }
}
下一步:您应该将类设置为依赖接口而不是实际的服务类。通过这种方式,您可以轻松创建另一个ServiceMock,当您实例化类时,该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();
    }
}

与您的要求相关:

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

这不是一个好的做法,它可能会变得非常肮脏。我没有这样的解决方案,因为您使用的是直接类型而不是接口


我不知道您是否正在处理一些遗留代码或第三方库,但通过编程到接口而不是类来解决此类问题是这里的关键。

再次强调这一点:

  • 如果您的
    new Service()
    构造函数正在连接到端点,您将无法模拟此代码。UnitTests永远不应该依赖于另一个可用的端点,即使之后您替换了该实例
  • 如果
    服务
    不提供可模拟的接口
    iSeries设备
    且其方法不是虚拟的,则您将无法对其进行模拟。您需要创建一个包装器接口和实现
一个常见的解决方法是创建一个具有受限可见性(例如内部)的第二个构造函数,并使用该构造函数将模拟注入到类中。您可以使用
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);
    }
}

再次强调这一点:

  • 如果您的
    new Service()
    构造函数正在连接到端点,您将无法模拟此代码。UnitTests永远不应该依赖于另一个可用的端点,即使之后您替换了该实例
  • 如果
    服务
    不提供可模拟的接口
    iSeries设备
    且其方法不是虚拟的,则您将无法对其进行模拟。您需要创建一个包装器接口和实现
一个常见的解决方法是创建一个具有受限可见性(例如内部)的第二个构造函数,并使用该构造函数将模拟注入到类中。您可以使用
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);
    }
}

服务是一个实例,所以您不能模拟它。服务应该实现一个接口,然后你们可以模拟它,你们真的应该在那个时候注入它point@BigDaddy我可以创建一个服务可以实现的接口,但是通过ctor注入,创建服务的工作被转移到其他地方。客户的意图是创建服务。这不是客户“使用”服务的意图吗。你需要依赖于抽象,而不是具体。这样做也不需要DI容器。客户端的实例化器可以注入服务。若你们想做单元测试,你们需要注入你们的依赖项。服务是一个实例,所以你们不能模仿它。服务应该实现一个接口,然后你们可以模拟它,你们真的应该在那个时候注入它point@BigDaddy我可以创建一个服务可以实现的接口,但是通过ctor注入,创建服务的工作被转移到其他地方。客户的意图是创建服务。这不是客户“使用”服务的意图吗。你需要依赖于抽象,而不是具体。这样做也不需要DI容器。客户端的实例化器可以注入服务。如果你想做单元测试,你需要注入你的依赖项。你能扩展一下“类不应该实例化服务”吗?客户机的意图是私下处理服务,所以没有其他类需要了解服务或与服务耦合。但是,你不能嘲笑它。也许你可以在上面添加一些模式,也许是一些工厂。但是如果“没有其他类需要知道发生了什么”,它看起来像是封装的,因此您的测试也不应该知道它。问题不是其他类可以知道您的服务。问题是,现在的情况是,您的类除了具体的服务之外什么都不知道。因此,您不能将其解耦并使用另一个“假服务”代替