C# 为使用RestClient的实用程序编写单元测试

C# 为使用RestClient的实用程序编写单元测试,c#,unit-testing,restsharp,C#,Unit Testing,Restsharp,我们已经决定创建一个可以跨多个项目重用的HTTP记录器,我们创建了一个如下所示的实用程序 // pseudo-code public class HttpLog{ private readonly string uri; private readonly RestClient client; public HttpLog(string uri){ this.uri = uri; // notice the initia

我们已经决定创建一个可以跨多个项目重用的HTTP记录器,我们创建了一个如下所示的实用程序

// pseudo-code
public class HttpLog{
      private readonly string uri;
      private readonly RestClient client;

      public HttpLog(string uri){
          this.uri = uri;
          // notice the initialization of rest client
          this.client = new RestClient();
      }

     void write(object data){
         this.client.uri = this.uri + endpoint;
         this.client.postAsync(data);
     }           
}
使用者应该提供URI,并且我们已经公开了记录数据的公共写入方法,但是,我们无法对我们的
HttpLog
类进行单元测试,因为它初始化了rest客户端。我们没有使用依赖注入,因为我们正在创建实用程序

任何关于如何重构或单元测试的帮助都将不胜感激。
write()
method

我们可以想出两种方法

  • 构造函数重载(这不是进行单元测试的有效方法)
  • 将客户机属性设置为公共{get;set},这也违反了OOP原则
请告诉我们是否有更好的方法来单元测试此代码

下面的答案说明使用构造函数重载或将属性公开

为什么我不喜欢依赖注入或构造函数重载,因为我坚信消费者/客户不应该关心或担心实现细节。它们应该尽可能多。如果在构造函数重载中使用它们,那么就是在制造一种污染抽象的方式

例如,如果您使用的是RestClient或HttpClient,他们不会要求您提供有关如何编写数据的HTTP实现,他们只是要求您发布URI和数据,这才是对最终用户的真正抽象


如果我的假设是错误的,请纠正我首先:你在测试什么?您的方法,还是REST服务本身

既然您说“我们无法对我们的HttpLog类进行单元测试”,那么我推断您正在尝试测试您的类

因此,您应该在不使用REST服务(这是一个外部依赖项)的情况下测试它。REST客户机应该作为依赖项注入,这样就可以轻松地模拟它

我们没有使用依赖注入,因为我们正在创建实用程序

这不是跳过依赖项注入的有效参数

注意:我从您的陈述中推断,您知道如何实现依赖项注入,您只是选择了不这样做。我将省略依赖注入的一个实际例子,因此这个答案可以集中在核心问题上:您决定不使用依赖注入

  • 构造函数重载(这不是进行单元测试的有效方法)
  • 这违背了测试的目的。您正在为测试和(真实)运行时创建不同的代码路径,这意味着测试不再(完全)测试运行时代码的执行

    目前,您的构造函数只实例化rest客户机,因此您没有做太多妥协。但是,如果构造函数做的不仅仅是这些,那么同样的情况就不适用了。其次,如果测试的构造函数与运行时实际使用的构造函数不同,则无法检测任何回归

  • 将客户机属性设置为公共{get;set},这也违反了OOP原则
  • 你的第二个建议直接证明你愿意(并试图)注入依赖性。您只是试图通过一个公共可设置的属性而不是构造函数参数来注入它

    您认为使用可公开设置的属性不是一个好的决定,这是正确的,因为它为其他问题打开了大门。
    相比之下,使用构造函数参数可以实现相同的功能(公开选择客户机),而不会影响封装(无法在对象的生命周期内更改客户机)


    因此,答案是首先使用依赖注入:你在测试什么?您的方法,还是REST服务本身

    既然您说“我们无法对我们的HttpLog类进行单元测试”,那么我推断您正在尝试测试您的类

    因此,您应该在不使用REST服务(这是一个外部依赖项)的情况下测试它。REST客户机应该作为依赖项注入,这样就可以轻松地模拟它

    我们没有使用依赖注入,因为我们正在创建实用程序

    这不是跳过依赖项注入的有效参数

    注意:我从您的陈述中推断,您知道如何实现依赖项注入,您只是选择了不这样做。我将省略依赖注入的一个实际例子,因此这个答案可以集中在核心问题上:您决定不使用依赖注入

  • 构造函数重载(这不是进行单元测试的有效方法)
  • 这违背了测试的目的。您正在为测试和(真实)运行时创建不同的代码路径,这意味着测试不再(完全)测试运行时代码的执行

    目前,您的构造函数只实例化rest客户机,因此您没有做太多妥协。但是,如果构造函数做的不仅仅是这些,那么同样的情况就不适用了。其次,如果测试的构造函数与运行时实际使用的构造函数不同,则无法检测任何回归

  • 将客户机属性设置为公共{get;set},这也违反了OOP原则
  • 你的第二个建议直接证明你愿意(并试图)注入依赖性。您只是试图通过一个公共可设置的属性而不是构造函数参数来注入它

    您认为使用可公开设置的属性不是一个好的决定,这是正确的,因为它为其他问题打开了大门。
    相比之下,使用构造函数参数可以实现相同的功能(公开选择客户机),而不会影响封装(无法在对象的生命周期内更改客户机)

    因此,答案是使用依赖注入

    我们没有使用依赖注入,因为我们正在创建utilit
    public class HttpLog{
          private readonly string uri;
          private readonly RestClient client;
    
          public HttpLog(string uri) : this(uri, new RestClient()){
          }
    
          public HttpLog(string uri, RestClient restClient){
              this.uri = uri;
              // notice the initialization of rest client
              this.client = restClient;
          }
    
         void write(object data){
             this.client.uri = this.uri + endpoint;
             this.client.postAsync(data);
         }           
    }