简体   繁体   English

N仅替代某些类型的自动模拟

[英]NSubstitute auto-mocking certain types only

I've noticed that NSubstitute auto-mocks the following types: 我注意到NSubstitute会自动修改以下类型:

  • Array 数组
  • IObservable 可观察的
  • String
  • Task 任务

Although I know this happens, I can't find the reason for picking these types. 尽管我知道这种情况会发生,但我找不到选择这些类型的原因 Why not leave String null? 为什么不让String null? Or include collections instead of just Array ? 还是包括集合而不只是Array

For example: 例如:

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");

All get a mock implemented for them, whereas List<T> would be null (or any other reference type). 所有人都为其实现了一个模拟,而List<T>将为null(或任何其他引用类型)。

The source code gives no indication as to the reason just implements to auto-mocks in the AutoValues folder. 源代码未提供有关仅实现AutoValues文件夹中自动装箱的原因的指示。

Short answer: specific auto-mocked types just got added as people wanted them and we felt the convenience would outweigh any confusion caused by returning a real value when a mock or default(T) may have been expected. 简短的答案:随着人们的需要,添加了特定的自动模拟类型,我们认为,当期望模拟或default(T)时,返回实际值所带来的任何混淆都将超过其便利性。


In case anyone's interested here's the longer, hopefully-partially-accurate story of this feature's evolution as I recall it. 如果有人对此感兴趣的话,我记得它是关于此功能演变的更长的,希望是部分准确的故事。

NSubstitute started off returning default(T) for everything. NSubstitute开始返回所有内容的default(T) This gets painful for cases where we have chains of calls, so we added auto-mocking for interface types. 对于我们拥有呼叫链的情况,这会很痛苦,因此我们为接口类型添加了自动模拟功能。 This is a fairly safe operation - unlike with classes there is no potential for real code to run, say, from the constructor call, or by inadvertently calling a non-virtual method. 这是一个相当安全的操作-与类不同,没有实际代码可以通过构造函数调用或无意间调用非虚拟方法运行的可能性。 For example, mySub.SomeAutoSub().DoStuff() could run real code or not based on the return type of SomeAutoSub() . 例如, mySub.SomeAutoSub().DoStuff()是否可以基于SomeAutoSub()的返回类型运行真实代码。

The next level of safety is classes that have all virtual members, and a default constructor. 更高级别的安全性是具有所有虚拟成员和默认构造函数的类。 The documentation refers to theses as pure virtual classes . 该文档将这些论文称为纯虚拟类 Having virtual members means if we dig in to an auto-subbed instance we can't accidentally call real code. 拥有虚拟成员意味着,如果我们深入研究自动修改实例,就不会意外调用真实代码。 Now this will still run real code via the constructor, but it doesn't take parameters and so we can assume (read: cross-fingers and hope) the default behaviour will do something sensible rather than doing anything terrible. 现在,它仍将通过构造函数运行真实代码,但是它不带参数,因此我们可以假设(请读:交叉手指和希望)默认行为将做明智的事情,而不是可怕的事情。

Finally, there are the exceptional cases you pointed out like Array and Task . 最后,您指出了一些例外情况,例如ArrayTask These were added completely ad hoc based on feedback when we felt the convenience would outweigh any confusion caused. 当我们认为方便会超过造成的任何混乱时,会根据反馈完全临时添加这些内容。 The main thing we want to avoid with these is returning a real value when a mock was expected. 我们要避免的主要事情是在期望模拟时返回真实值。 NSubstitute doesn't work well if you accidentally call .Returns on something that isn't a mock. 如果您不小心调用了.Returns ,它将无法正常工作。返回不是模拟的东西。

Strings , Arrays and Tasks meet this criteria. StringsArraysTasks符合此条件。 You can't mock these types so having a default value that works sensibly in tests seems reasonable. 您无法模拟这些类型,因此拥有在测试中合理工作的默认值似乎是合理的。 I found I often ran into string ops that blew up because of the null default, so I added that one for convenience. 我发现我经常遇到字符串操作,因为默认为null,该字符串操作会崩溃,因此为了方便起见,我添加了一个。 Others wanted a default Task that worked sensibly and so added that. 其他人想要一个明智的默认Task ,因此添加了它。 I can't remember the reasoning behind not auto-subbing collections (possibly because some people use mocked versions of IList<T> , where providing a specific implementation like List<T> is easier enough to explicitly stub?). 我不记得没有自动对集合进行分类的原因(可能是因为某些人使用了IList<T>模拟版本,在其中提供像List<T>这样的特定实现更容易进行显式存根?)。

I think we can get away with some inconsistency here because we are in the context of tests -- if we need a specific value or behaviour we'll explicitly stub it out. 我认为我们可以在这里摆脱一些不一致之处,因为我们处于测试的环境中-如果我们需要特定的值或行为,则将其明确地存根。 Otherwise we'll get a default value that hopefully won't get in the way. 否则,我们将获得一个默认值,希望它不会造成任何影响。

If you would like additional things auto-subbed please ping the discussion group . 如果您希望自动添加其他内容,请ping 讨论组 It's a bit harder to make potentially-breaking changes like this now but if there's a compelling case for a particular type we can look at adding it in. 现在要像这样进行可能具有重大突破的更改会有点困难,但是如果有特殊类型的令人信服的案例,我们可以考虑添加它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM