如何在不中断正在测试的代码的情况下注入假货

How to inject a fake without disrupting the code being tested

提问人:bokabokaboka 提问时间:10/30/2023 最后编辑:ndc85430bokabokaboka 更新时间:10/30/2023 访问量:51

问:

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

  • 在这种技术中,您需要分解触及 file系统添加到一个单独的类中。这样你就可以很容易地区分 它,然后替换测试中对该类的调用(就像原来一样 如图 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
 }
}
  • 接下来,您可以告诉被测类,而不是使用 具体的 FileExtensionManager 类,它将处理某种形式的 ExtensionManager,但不知道它的具体实现。 In.NET,这可以通过使用基类或 FileExtensionManager 将扩展的接口。下一个上市 展示了在设计中使用新界面以使其更 可测试。图 3.4 显示了此实现的示意图。

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

public class FileExtensionManager : IExtensionManager
 {
 public bool IsValid(string fileName)
 {
 //read some file here
 }
 }
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);
 }

请注意,“IsValidLogFileName”方法中的“IExtensionManager mgr = new FileExtensionManager();”会导致稍后出现问题。

清单 3.4 使用构造函数注入注入存根

public class LogAnalyzer
{
    private IExtensionManager manager;
    public LogAnalyzer(IExtensionManager mgr)
    {
    manager = mgr;
    }
    public bool IsValidLogFileName(string fileName)
    {
    return manager.IsValid(fileName);  //The tested code has been disrupted; 'IExtensionManager mgr = new FileExtensionManager();' has been deleted!!!!!
    }
}
public interface IExtensionManager
{
    bool IsValid(string fileName);
}


[TestFixture]
public class LogAnalyzerTests
{
    [Test]
    public void
    IsValidFileName_NameSupportedExtension_ReturnsTrue()
    {
    FakeExtensionManager myFakeManager =
    new FakeExtensionManager();
    myFakeManager.WillBeValid = true;
    LogAnalyzer log =
    new LogAnalyzer (myFakeManager);
    bool result = log.IsValidLogFileName("short.ext");
    Assert.True(result);
    }
    }
internal class FakeExtensionManager : IExtensionManager
{
    public bool WillBeValid = false;
    public bool IsValid(string fileName)
    {
    return WillBeValid;
    }
}

请注意上面的 IsValidLogFileName 方法。虽然修改允许测试代码正常运行,但测试代码已被中断;“IExtensionManager mgr = new FileExtensionManager();”已被删除。

这会破坏源代码功能的完整性。当我们正常运行源代码时,程序将无法正确调用 FileExtensionManager 类的 IsValid 方法!您不会得到清单 3.2 中所示的相同结果。如果我想恢复源代码,我应该在第 3.4 行中将“IExtensionManager mgr = new FileExtensionManager();”放在什么位置?以便代码在运行时调用 FileExtensionManager 类的 IsValid 方法,并在测试期间调用 FakeExtensionManager 类的 IsValid 方法

C# 单元测试 依赖项注入 nunit

评论


答:

1赞 David Osborne 10/30/2023 #1

无论你用什么来创建一个新的实例,都应该创建一个类的适当实例,该类通过构造函数实现和注入它。LogAnalyzerIExtensionManager

就像你的测试正在实例化和注入一个假的一样,你的真实代码应该实例化并注入一个真实的实例。这可以使用 IoC 容器或“纯 DI”。

要回答该问题,请执行以下操作:

...我应该把'IExtensionManager mgr = new FileExtensionManager()放在哪里?

您可以在“组合根”中执行此操作。

评论

0赞 bokabokaboka 10/31/2023
IExtensionManager FEM = new FileExtensionManager();LogAnalyzer 日志 = 新的 LogAnalyzer (FEM);使用“日志。IsValidLogFileName(“short.ext”);'在主程序中实现文件读取。?
0赞 David Osborne 10/31/2023
是的,如果主程序是您编写对象的地方。