为什么提取接口后可以替换底层实现?

问题描述 投票:0回答:1

以下是《单元测试的艺术,第二版》一书的原文: 提取接口以允许替换底层实现

  • 在这种技术中,您需要将涉及文件系统的代码分解为一个单独的类。 这样您就可以轻松地区分它,然后从测试中替换对该类的调用(如图 3.3 所示)。第一个清单显示了您需要更改代码的位置。

清单 3.1 提取一个涉及文件系统的类并调用它

public bool IsValidLogFileName(string fileName)
{
 FileExtensionManager mgr =
 new FileExtensionManager();
 return mgr.IsValid(fileName);
}
class FileExtensionManager
 {
 public bool IsValid(string fileName)
 {
 //read some file here
 }
 }
  • 接下来,您可以告诉被测试的类,它将处理某种形式的 ExtensionManager,而不是使用具体的 FileExtensionManager 类,而不知道其具体实现。在 .NET 中,这可以通过使用基类或 FileExtensionManager 扩展的接口来完成。下一个清单显示了在设计中使用新接口以使其更易于测试。图 3.4 显示了该实现的示意图。

清单 3.2 从已知类中提取接口

public class FileExtensionManager : IExtensionManager
 {
 public bool IsValid(string fileName)
 {
 ...
 }
 }
public interface IExtensionManager
 {
 bool IsValid (string fileName);
 }
//the unit of work under test:
public bool IsValidLogFileName(string fileName)
{
 IExtensionManager mgr =
 new FileExtensionManager();
 return mgr.IsValid(fileName);
 }
  • 您使用一个 IsValid(字符串)方法创建一个接口,并让 FileExtensionManager 实现该接口。它的工作方式仍然完全相同,只是现在您可以用自己的“假”管理器替换“真实”管理器,稍后您将创建该管理器来支持您的测试。您还没有创建存根扩展管理器,所以让我们立即创建它。它显示在下面的列表中

问题: 我不明白为什么添加接口可以更容易区分和替换后面的类调用?这个问题属于设计模式的范围吗?我应该学习一些设计模式来更好地理解这个问题吗?

c# unit-testing design-patterns junit nunit
1个回答
0
投票

不明白为什么添加接口可以更方便以后区分和替换类调用?

考虑到有限的例子,这是一种可以理解的混乱。

IsValidLogFileName
方法仍然与
FileExtensionManager
紧密耦合,因为它正在调用其构造函数。

在第一个和第二个示例中,如果您想更改为不同类型的扩展管理器,则需要更改一行代码。

这个问题属于设计模式的范围吗?我应该学习一些设计模式来更好地理解这个问题吗?

是的。查看依赖注入。如果

IsValidLogFileName
使用提供给它的
IExtensionManager
对象,而不是直接创建
new FileExtensionManager()
,那么您将完全从其代码中消除对 FileExtensionManager 的引用。

您可以通过简单地传入不同的对象来将

FileExtensionManager
替换为
BlobExtensionManager
FakeExtensionManager
EmptyExtensionManager
。您的方法的代码根本不需要更改!

您可以编写使用 mock

IExtensionManager
的单元测试来指定
IsValid
对于不同的值应返回什么,以验证
IsValidLogFileName
的行为是否符合其在各种情况下的行为方式。

© www.soinside.com 2019 - 2024. All rights reserved.