NSubstitute 仅自动模拟某些类型

NSubstitute auto-mocking certain types only

我注意到 NSubstitute 会自动模拟以下类型:

虽然我知道会发生这种情况,但我找不到选择这些类型的原因。为什么不让 String 为空?或者包括集合而不仅仅是 Array?

例如:

var crazyInterface = Substitute.For<ICrazyInterface>();

FileInfo[] folderContents = crazyInterface.FolderContents(@"C:\folder");
IObservable<FileInfo> fileObserver = crazyInterface.CreateNewFileObservable();
string fileHash = crazyInterface.FileHash(@"C:\folder\file.txt");
Task<byte[]> fileContents = crazyInterface.ReadFileContentsAsync(@"C:\folder\file.txt");

所有人都为他们实现了一个模拟,而 List<T> 将为空(或任何其他引用类型)。

source code gives no indication as to the reason just implements to auto-mocks in the AutoValues 文件夹。

简短回答:特定的自动模拟类型只是根据人们的需要添加的,我们认为这种便利性会超过 return 当模拟或 default(T)已经预料到。


如果有人对此感兴趣,这里是我记得的有关此功能演变的更长的、希望部分准确的故事。

NSubstitute 开始 returning default(T) 一切。对于我们有调用链的情况,这会很痛苦,因此我们为接口类型添加了自动模拟。这是一个相当安全的操作 - 与 类 不同,真实代码不可能 运行,例如,从构造函数调用或无意中调用非虚拟方法。例如,mySub.SomeAutoSub().DoStuff() 可以 运行 真实代码,也可以不基于 SomeAutoSub() 的 return 类型。

下一个安全级别是 类,它具有所有虚拟成员和默认构造函数。文档将这些论文称为 pure virtual classes。拥有虚拟成员意味着如果我们深入研究一个自动替换的实例,我们就不会意外地调用真实代码。现在这仍然是 运行 通过构造函数的真实代码,但它不接受参数,因此我们可以假设(阅读:交叉手指并希望)默认行为会做一些明智的事情而不是做任何可怕的事情。

最后,还有您指出的例外情况,例如 ArrayTask。当我们觉得方便会超过造成的混乱时,这些是根据反馈完全临时添加的。我们要避免的主要事情是 return 在预期模拟时使用真实值。如果您不小心调用了非模拟对象的 .Returns,NSubstitute 将无法正常工作。

StringsArraysTasks 符合此条件。您不能模拟这些类型,因此拥有一个在测试中合理工作的默认值似乎是合理的。我发现我经常 运行 进入由于空默认值而爆炸的字符串操作,所以为了方便我添加了那个。其他人想要一个合理的默认值 Task,因此添加了它。我不记得不自动替换集合背后的原因(可能是因为有些人使用 IList<T> 的模拟版本,其中提供像 List<T> 这样的特定实现更容易显式存根?)。

我认为我们可以避开这里的一些不一致,因为我们处于测试环境中——如果我们需要特定的值或行为,我们会明确地将其删除。否则我们将获得一个默认值,希望不会妨碍。

如果您想自动替换其他内容,请 ping discussion group。现在进行这样的潜在破坏性更改有点困难,但如果对特定类型有令人信服的案例,我们可以考虑添加它。