NSubstitute完全手册(十五)自动递归模拟

简介:

替代实例一旦被设置属性或方法,则将自动返回非NULL值。例如,任何属性或方法如果返回接口、委托或纯虚类*,则将自动的返回替代实例自身。通常这被称为递归模拟技术,而且是非常实用的。比如其可以避免显式地设置每个替代实例,也就意味着更少量的代码。诸如String和Array等类型,默认会返回空值而不是NULL。

*注:一个纯虚类是指一个类,其所有的公有方法和属性都被定义为virtual或abstract,并且其具有一个默认公有或受保护地无参构造函数。

递归模拟

比如说我们有如下类型定义:

复制代码
1     public interface INumberParser
2     {
3       int[] Parse(string expression);
4     }
5     public interface INumberParserFactory
6     {
7       INumberParser Create(char delimiter);
8     }
复制代码

我们想配置 INumberParserFactory 来创建一个解析器,该解析器会为一个 expression 返回一定数量的 int 类型的值。我们可以手工创建每个替代实例:

复制代码
 1     [TestMethod]
 2     public void Test_AutoRecursiveMocks_ManuallyCreateSubstitutes()
 3     {
 4       var factory = Substitute.For<INumberParserFactory>();
 5       var parser = Substitute.For<INumberParser>();
 6       factory.Create(',').Returns(parser);
 7       parser.Parse("an expression").Returns(new int[] { 1, 2, 3 });
 8 
 9       var actual = factory.Create(',').Parse("an expression");
10       CollectionAssert.AreEqual(new int[] { 1, 2, 3 }, actual);
11     }
复制代码

或者可以应用递归模拟功能,INumberParserFactory.Create() 会自动返回 INumberParser 类型的替代实例。

复制代码
1     [TestMethod]
2     public void Test_AutoRecursiveMocks_AutomaticallyCreateSubstitutes()
3     {
4       var factory = Substitute.For<INumberParserFactory>();
5       factory.Create(',').Parse("an expression").Returns(new int[] { 1, 2, 3 });
6 
7       var actual = factory.Create(',').Parse("an expression");
8       CollectionAssert.AreEqual(new int[] { 1, 2, 3 }, actual);
9     }
复制代码

每次当使用相同参数调用一个被递归模拟的属性或方法时,都会返回相同的替代实例。如果使用不同参数调用,则将会返回一个新的替代实例。

复制代码
 1     [TestMethod]
 2     public void Test_AutoRecursiveMocks_CallRecursivelySubbed()
 3     {
 4       var factory = Substitute.For<INumberParserFactory>();
 5       factory.Create(',').Parse("an expression").Returns(new int[] { 1, 2, 3 });
 6 
 7       var firstCall = factory.Create(',');
 8       var secondCall = factory.Create(',');
 9       var thirdCallWithDiffArg = factory.Create('x');
10 
11       Assert.AreSame(firstCall, secondCall);
12       Assert.AreNotSame(firstCall, thirdCallWithDiffArg);
13     }
复制代码

注:不会为类创建递归的替代实例,因为创建和使用类可能有潜在的或多余的副作用。因此,有必要显式地创建和返回类的替代实例

替代链

当需要时,我们可以使用递归模拟来简单地设置替代链,但这并不是一个理想的做法。例如:

复制代码
 1     public interface IContext
 2     {
 3       IRequest CurrentRequest { get; }
 4     }
 5     public interface IRequest
 6     {
 7       IIdentity Identity { get; }
 8       IIdentity NewIdentity(string name);
 9     }
10     public interface IIdentity
11     {
12       string Name { get; }
13       string[] Roles();
14     }
复制代码

如果要获取 CurrentRequest 中的 Identity 并返回一个名字,我们可以手工为 IContext、IRequest 和 IIdentity 创建替代品,然后使用 Returns() 将这些替代实例链接到一起。或者我们可以使用为属性和方法自动创建的替代实例。

复制代码
1     [TestMethod]
2     public void Test_AutoRecursiveMocks_SubstituteChains()
3     {
4       var context = Substitute.For<IContext>();
5       context.CurrentRequest.Identity.Name.Returns("My pet fish Eric");
6       Assert.AreEqual(
7         "My pet fish Eric",
8         context.CurrentRequest.Identity.Name);
9     }
复制代码

在这里 CurrentReques t是自动返回一个 IRequest 的替代实例,IRequest 替代实例会自动返回一个 IIdentity 替代实例。

注:类似于这种设置很长的替代实例链,一般被认为是代码臭味:我们打破了 Law of Demeter 原则,对象只应该与其直接关系的临近对象打交道,而不与临近对象的临近对象打交道。如果你写的测试用例中没有使用递归模拟,设置的过程可能会明显的变复杂,所以如果要使用递归模式,则需要格外的注意类似的类型耦合。

自动值

当属性或方法返回 String 或 Array 类型的值时,默认会返回空或者非 NULL 值。比如在你仅需要返回一个对象引用,但并不关心其特定的属性时,这个功能可以帮你避免空引用异常。

复制代码
1     [TestMethod]
2     public void Test_AutoRecursiveMocks_AutoValues()
3     {
4       var identity = Substitute.For<IIdentity>();
5       Assert.AreEqual(string.Empty, identity.Name);
6       Assert.AreEqual(0, identity.Roles().Length);
7     }
复制代码

NSubstitute 完全手册




本文转自匠心十年博客园博客,原文链接:http://www.cnblogs.com/gaochundong/archive/2013/05/22/nsubstitute_auto_and_recursive_mocks.html,如需转载请自行联系原作者
目录
相关文章
|
人工智能
客户在哪儿 AI:如何用最少场次的活动覆盖最多的目标客户
在ToB市场,线下活动是高效获客的关键。面对面交流增进信任,潜在客户集中,直接展示产品能缩短销售路径。然而,高成本和低效活动是挑战。通过分析目标客户历史活动数据,客户在哪儿AI帮助企业精准定位,以最少投入触达最多潜在客户,如仅10场活动即可覆盖44.9%年营收客户。
|
SQL 监控 关系型数据库
mysql统计数据库大小
通过这些方法,数据库管理员可以有效地监控和规划MySQL数据库的存储需求,确保数据库的稳定运行。
509 3
|
关系型数据库 MySQL
Mysql 查询以某个字符开头的语句和LIKE的使用
Mysql 查询以某个字符开头的语句和LIKE的使用
279 0
|
12月前
|
数据可视化 BI UED
帆软 BI 怎么制作桑基图
帆软 BI 怎么制作桑基图
204 0
|
开发框架 小程序 数据可视化
基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop
基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop
263 0
|
SQL 存储 数据库
SqlAlchemy 2.0 中文文档(九)(1)
SqlAlchemy 2.0 中文文档(九)
83 0
|
安全 Shell API
Docker未授权访问漏洞利用
Docker未授权访问漏洞利用
552 0
|
Linux 芯片
Linux中断处理机制
中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。
Linux中断处理机制
|
开发框架 前端开发 区块链
(bsc)币安链上合约DAPP项目源码开发解决方案
function transferAsset(address receiver, uint256 amount) public returns (bool success) { // 检查资产数量是否足够 if (assetValue &lt; amount) { return false;