我们决定创建一个可以在多个项目中重复使用的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
类进行单元测试。我们正在创建实用程序,因此没有使用依赖注入。
在如何重构或单元测试。write()
方法方面,任何帮助将不胜感激。
我们可以想到两种方法
请告诉我们是否有更好的方法对该代码进行单元测试。
下面给出的答案是使用构造函数重载或将该属性设置为公开
为什么我不喜欢依赖注入或构造函数重载,因为我坚信消费者/客户不应该关心或担心实现细节。它们应尽可能地抽象。如果使它们处于构造函数重载中,那么您正在寻找一种污染抽象的方法。
例如,如果您使用RestClient或HttpClient,他们不要求您提供有关如何编写数据的HTTP实现,他们只是询问您URI和要发布的数据,这才是对最终用户的真正抽象。
如果我的假设错误,请纠正我
答案 0 :(得分:1)
第一件事:要测试什么?您的方法,还是REST服务本身?
由于您说“我们无法对HttpLog类进行单元测试” ,因此我推断您正在尝试测试您的类。
因此,您应该在没有REST服务的情况下对其进行测试[em] (这是外部依赖项)。 REST客户端应作为依赖项注入,因此可以轻松对其进行模拟。
由于我们正在创建实用程序,因此我们不使用依赖项注入。
这不是跳过依赖项注入的有效参数。
注意:我从您的陈述中推断您知道如何实现依赖注入,您只是选择了不这样做。我将省略一个依赖注入的实际示例,因此该答案可以集中在核心问题上:您决定不使用依赖注入。
- 构造函数重载(这不是仅用于单元测试的有效方法)
这破坏了测试的重点。您将为测试和(实际)运行时创建不同的代码路径,这意味着测试不再(完全)测试运行时代码执行。
当前,您的构造函数仅实例化其余客户端,因此您并没有太多妥协。但是,如果构造函数所做的不止于此,那将不适用。其次,如果要测试与运行时实际使用的构造函数不同的构造函数,则将无法检测到任何回归。
- 将客户财产设为公开{get; set}也违反了OOP原则。
您的第二条建议直接证明您愿意(并尝试)注入依赖项。您只是试图通过可公开设置的属性(而不是构造函数参数)注入它。
您正确地认为,使用可公开设置的属性不是一个好决定,因为它为其他问题打开了大门。
相比之下,使用构造函数参数可以实现相同的功能(公开选择客户端),而不会损害封装(在对象的生存期内无法更改客户端)。
因此,答案是使用依赖项注入。
答案 1 :(得分:1)
由于我们正在创建实用程序,因此我们不使用依赖项注入。
那不是原因。如果使用依赖项并希望能够对其进行正确测试,则应使用依赖项注入。这并不意味着您不能为普通用户提供默认的实现。
提供构造函数重载。我不知道你为什么认为这将是“低效的”。
示例:
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);
}
}