NSubstitute 仅自动模拟某些类型
NSubstitute auto-mocking certain types only
我注意到 NSubstitute 会自动模拟以下类型:
- 数组
- IObservable
- 字符串
- 任务
虽然我知道会发生这种情况,但我找不到选择这些类型的原因。为什么不让 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。拥有虚拟成员意味着如果我们深入研究一个自动替换的实例,我们就不会意外地调用真实代码。现在这仍然是 运行 通过构造函数的真实代码,但它不接受参数,因此我们可以假设(阅读:交叉手指并希望)默认行为会做一些明智的事情而不是做任何可怕的事情。
最后,还有您指出的例外情况,例如 Array
和 Task
。当我们觉得方便会超过造成的混乱时,这些是根据反馈完全临时添加的。我们要避免的主要事情是 return 在预期模拟时使用真实值。如果您不小心调用了非模拟对象的 .Returns
,NSubstitute 将无法正常工作。
Strings
、Arrays
和 Tasks
符合此条件。您不能模拟这些类型,因此拥有一个在测试中合理工作的默认值似乎是合理的。我发现我经常 运行 进入由于空默认值而爆炸的字符串操作,所以为了方便我添加了那个。其他人想要一个合理的默认值 Task
,因此添加了它。我不记得不自动替换集合背后的原因(可能是因为有些人使用 IList<T>
的模拟版本,其中提供像 List<T>
这样的特定实现更容易显式存根?)。
我认为我们可以避开这里的一些不一致,因为我们处于测试环境中——如果我们需要特定的值或行为,我们会明确地将其删除。否则我们将获得一个默认值,希望不会妨碍。
如果您想自动替换其他内容,请 ping discussion group。现在进行这样的潜在破坏性更改有点困难,但如果对特定类型有令人信服的案例,我们可以考虑添加它。
我注意到 NSubstitute 会自动模拟以下类型:
- 数组
- IObservable
- 字符串
- 任务
虽然我知道会发生这种情况,但我找不到选择这些类型的原因。为什么不让 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。拥有虚拟成员意味着如果我们深入研究一个自动替换的实例,我们就不会意外地调用真实代码。现在这仍然是 运行 通过构造函数的真实代码,但它不接受参数,因此我们可以假设(阅读:交叉手指并希望)默认行为会做一些明智的事情而不是做任何可怕的事情。
最后,还有您指出的例外情况,例如 Array
和 Task
。当我们觉得方便会超过造成的混乱时,这些是根据反馈完全临时添加的。我们要避免的主要事情是 return 在预期模拟时使用真实值。如果您不小心调用了非模拟对象的 .Returns
,NSubstitute 将无法正常工作。
Strings
、Arrays
和 Tasks
符合此条件。您不能模拟这些类型,因此拥有一个在测试中合理工作的默认值似乎是合理的。我发现我经常 运行 进入由于空默认值而爆炸的字符串操作,所以为了方便我添加了那个。其他人想要一个合理的默认值 Task
,因此添加了它。我不记得不自动替换集合背后的原因(可能是因为有些人使用 IList<T>
的模拟版本,其中提供像 List<T>
这样的特定实现更容易显式存根?)。
我认为我们可以避开这里的一些不一致,因为我们处于测试环境中——如果我们需要特定的值或行为,我们会明确地将其删除。否则我们将获得一个默认值,希望不会妨碍。
如果您想自动替换其他内容,请 ping discussion group。现在进行这样的潜在破坏性更改有点困难,但如果对特定类型有令人信服的案例,我们可以考虑添加它。