简体   繁体   English

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

[英]Writing unit test for utility which consumes RestClient

We have decided to create an HTTP logger which can be reused across multiple projects, we created a utility which looks like this. 我们决定创建一个可以在多个项目中重复使用的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);
     }           
}

The consumer should provide the URI and we have exposed public write methods to log the data, however, we are unable to unit test our HttpLog class since it initializes the rest client. 使用者应提供URI,并且我们公开了公共写入方法来记录数据,但是,由于它初始化了其余客户端,因此我们无法对HttpLog类进行单元测试。 We are not using dependency injection since we are creating utility. 因为我们正在创建实用程序,所以我们没有使用依赖注入。

Any help would be greatly appreciated on how to refactor or unit test . 任何关于如何重构或单元测试的帮助将不胜感激。 write() method. write()方法。

We can think of two methods 我们可以想到两种方法

  • Constructor overload (which is not an efficient way just to unit test) 构造函数重载(这不是仅用于单元测试的有效方法)
  • making client property as public {get; 将客户财产设为公开{get; set} which also breaks OOP principle. set}也违反了OOP原则。

Please let us know if there is a better way to unit-test this code. 请让我们知道是否有更好的方法对该代码进行单元测试。

The answers below stated to use constructor overloading or making the property to public 下面的答案说明了使用构造函数重载或将该属性公开

Why I am not preferring dependency injection or constructor overload because of I strongly believe the consumer/client should not care or worry about the implementation details. 为什么我不喜欢依赖注入或构造函数重载,因为我坚信消费者/客户不应该关心或担心实现细节。 They should be as much as abstraction possible. 它们应尽可能地抽象。 If you make them in constructor overload then you are making a way to pollute the abstraction. 如果使它们处于构造函数重载中,那么您正在寻找一种污染抽象的方法。

For example, if you are using RestClient or HttpClient they don't ask you to provide HTTP implementation on how to write data, they simply ask you URI and data to post that is what a true abstraction is to end user. 例如,如果您使用RestClient或HttpClient,他们不要求您提供有关如何编写数据的HTTP实现,他们只是询问您URI和要发布的数据,这才是对最终用户的真正抽象。

Please correct me If my assumptions are wrong 如果我的假设是错误的,请纠正我

First things first: what are you testing? 首先: 什么测试? Your method, or the REST service itself? 您的方法,还是REST服务本身?

Since you say "we are unable to unit test our HttpLog class" , I infer you're trying to test your class. 因为您说“我们无法对HttpLog类进行单元测试” ,所以我推断您正在尝试测试您的类。

Therefore, you should test it without the REST service (which is an external dependency). 因此,您应该在使用REST服务(外部依赖项)的情况下对其进行测试。 The REST client should be injected as a dependency, so it can then easily be mocked. REST客户端应作为依赖项注入,因此可以轻松对其进行模拟。

We are not using dependency injection since we are creating utility. 因为我们正在创建实用程序,所以我们没有使用依赖注入。

That is not a valid argument for skipping dependency injection. 这对于跳过依赖项注入不是有效的参数。

Note: I infer from your statement that you know how to implement dependency injection, you've simply chosen not to. 注意:我从您的陈述中推断出您知道如何实现依赖注入,您只是选择了不这样做。 I'm going to omit an actual example of dependency injection so this answer can focus on the core problem: your decision not to use dependency injection. 我将省略一个依赖注入的实际示例,因此该答案可以集中在核心问题上:您决定不使用依赖注入。

  1. Constructor overload (which is not an efficient way just to unit test) 构造函数重载(这不是仅用于单元测试的有效方法)

This defeats the point of testing. 这破坏了测试的重点。 You're creating a different code path for your test and your (real) runtime, which means the test no longer (fully) tests the runtime code execution. 您正在为测试和(实际)运行时创建不同的代码路径,这意味着测试不再(完全)测试运行时代码执行。

Currently, your constructor only instantiates the rest client, so you're not compromising much. 当前,您的构造函数仅实例化其余客户端,因此您并没有太多妥协。 But the same would not apply if the constructor did more than just that. 但是,如果构造函数所做的不止于此,那将不适用。 Secondly, you'll be unable to detect any regressions if you're testing a different constructor than you actually use at runtime. 其次,如果您要测试与运行时实际使用的构造函数不同的构造函数,则将无法检测到任何回归。

  1. making client property as public {get; 将客户财产设为公开{get; set} which also breaks OOP principle. set}也违反了OOP原则。

Your second suggestion directly proves that you are willing (and trying) to inject a dependency . 您的第二个建议直接证明您愿意 (并尝试) 注入依赖项 You're simply trying to inject it via a publically settable property instead of a constructor parameter. 您只是试图通过可公开设置的属性(而不是构造函数参数)注入它。

You are correct that using a publically settable property is not a good decision, as it opens the door to other issues. 您正确地认为,使用可公共设置的属性不是一个好的决定,因为它为其他问题打开了大门。
Comparatively, using a constructor parameter allows the same functionality (publically choosing the client) without compromising the encapsulation (not being able to change the client during the object's lifetime). 相比之下,使用构造函数参数可以实现相同的功能(公开选择客户端),而不会损害封装(在对象的生存期内无法更改客户端)。

Therefore, the answer is to use dependency injection . 因此,答案是使用依赖注入

We are not using dependency injection since we are creating utility. 因为我们正在创建实用程序,所以我们没有使用依赖注入。

That is not a reason. 那不是原因。 If you use dependencies and want to be able to test it properly, you should use dependency injection. 如果使用依赖项并希望能够对其进行正确测试,则应使用依赖项注入。 That doesn't mean you cannot provide a default implementation for normal users. 这并不意味着您不能为普通用户提供默认的实现。

Provide a constructor overload. 提供构造函数重载。 I have no idea why you think this would be "inefficient". 我不知道你为什么认为这将是“低效的”。

Example: 例:

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

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

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