代码之家  ›  专栏  ›  技术社区  ›  user3205479

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

  •  0
  • user3205479  · 技术社区  · 7 年前

    我们决定创建一个可以在多个项目中重用的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() 方法

    我们可以想出两种方法

    • 构造函数重载(这不是进行单元测试的有效方法)
    • 将客户机属性设置为公共{get;set},这也违反了OOP原则。

    请告诉我们是否有更好的方法来单元测试这段代码。

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

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

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

    如果我的假设是错误的,请纠正我

    2 回复  |  直到 7 年前
        1
  •  1
  •   nvoigt    7 年前

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

    这不是理由。如果您使用依赖项并希望能够正确地测试它,那么应该使用依赖项注入。这并不意味着不能为普通用户提供默认实现。

    提供构造函数重载。我不知道你为什么认为这是“低效的”。

    例子:

    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);
         }           
    }
    
        2
  •  1
  •   Flater    7 年前

    第一件事: 什么 你在测试吗?您的方法,还是REST服务本身?

    既然你说 “我们无法对HttpLog类进行单元测试” ,我猜你是想考你的课。

    因此,你应该测试它 没有 REST服务(这是一个外部依赖项)。REST客户机应该作为依赖项注入,这样就可以很容易地对其进行模拟。

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

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

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

    1. 构造函数重载(这不是进行单元测试的有效方法)

    这违背了测试的意义。您正在为测试和(实际)运行时创建不同的代码路径,这意味着测试不再(完全)测试运行时代码的执行。

    目前,您的构造函数只实例化rest客户机,所以您没有做出太多让步。但如果构造器做的不止这些,同样的情况就不适用了。其次,如果测试的构造函数与运行时实际使用的构造函数不同,则无法检测任何回归。

    1. 将客户机属性设置为公共{get;set},这也违反了OOP原则。

    你的第二个建议直接证明你愿意 (正在努力) 注入依赖项 。您只是试图通过一个可公开设置的属性而不是构造函数参数来注入它。

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

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