NSubstitute完全手册(十七)参数匹配器上的操作

简介:

除了指定调用,当替代实例接收到了匹配的调用时,参数匹配器还可以用于执行特定的操作,并指定操作参数。这是一个相当罕见的需求,但在某些情况下可以使测试程序变得简单一些。

警告:一旦我们为替代实例添加有意义的行为,我们的测试和代码将面临过度指定和紧耦合的风险。对于类似的测试,通过抽象来更好地封装行为可能是更好的选择,甚至可以使用真实的协作对象并切换至 coarser-grained 测试。

调用回调函数

假设被测试类需要调用一个依赖对象的方法,并为其提供了一个回调函数,当依赖对象调用结束时通过回调来通知该类。当替代实例被调用时,我们可以使用 Arg.Invoke() 来立即调用这个回调。

让我们来看一个例子。比如说我们要测试 OrderPlacedCommand,其需要使用 IOrderProcessor 来处理订单,当处理成功地完成时使用 IEvents 来引发事件通知。

复制代码
 1     public interface IEvents
 2     {
 3       void RaiseOrderProcessed(int orderId);
 4     }
 5 
 6     public interface ICart
 7     {
 8       int OrderId { get; set; }
 9     }
10 
11     public interface IOrderProcessor
12     {
13       void ProcessOrder(int orderId, Action<bool> orderProcessed);
14     }
15 
16     public class OrderPlacedCommand
17     {
18       IOrderProcessor orderProcessor;
19       IEvents events;
20       public OrderPlacedCommand(IOrderProcessor orderProcessor, IEvents events)
21       {
22         this.orderProcessor = orderProcessor;
23         this.events = events;
24       }
25       public void Execute(ICart cart)
26       {
27         orderProcessor.ProcessOrder(
28             cart.OrderId,
29             wasOk => { if (wasOk) events.RaiseOrderProcessed(cart.OrderId); }
30         );
31       }
32     }
复制代码

在测试中,可以使用 Arg.Invoke() 来模拟 IOrderProcessor 处理订单结束的情况,并调用回调函数来通知调用方已经处理结束。

复制代码
 1     [TestMethod]
 2     public void Test_ActionsWithArgumentMatchers_InvokingCallbacks()
 3     {
 4       // Arrange
 5       var cart = Substitute.For<ICart>();
 6       var events = Substitute.For<IEvents>();
 7       var processor = Substitute.For<IOrderProcessor>();
 8       cart.OrderId = 3;
 9       // 设置 processor 当处理订单ID为3时,调用回调函数,参数为true
10       processor.ProcessOrder(3, Arg.Invoke(true));
11 
12       // Act
13       var command = new OrderPlacedCommand(processor, events);
14       command.Execute(cart);
15 
16       // Assert
17       events.Received().RaiseOrderProcessed(3);
18     }
复制代码

这里我们构造了 processor,用于处理 ID 为 3 的订单,并调用回调函数。我们使用 Arg.Invoke(true) 来传递 true 给回调函数。

Arg.Invoke 有几个重载方法,可以用于调用参数数量和类型不同的回调函数。我们也可以使用 Arg.InvokeDelegate 来调用定制的委托类型(那些不只是简单的 Action 类型的委托)。

执行带参数的操作

有时我们可能不想立即就调用回调函数。或者可能我们想存储所有的实例到一个特殊的参数,并将该参数传递给一个方法。甚至我们只是想捕获某个参数,以便后期查看。我们可以使用 Arg.Do 来完成这些目的。Arg.Do 会为每个匹配的调用来执行给定参数的操作。

复制代码
 1     public interface ICalculator
 2     {
 3       int Multiply(int a, int b);
 4     }
 5 
 6     [TestMethod]
 7     public void Test_ActionsWithArgumentMatchers_PerformingActionsWithArgs()
 8     {
 9       var calculator = Substitute.For<ICalculator>();
10 
11       var argumentUsed = 0;
12       calculator.Multiply(Arg.Any<int>(), Arg.Do<int>(x => argumentUsed = x));
13 
14       calculator.Multiply(123, 42);
15 
16       Assert.AreEqual(42, argumentUsed);
17     }
复制代码

这里,Multiply 方法的第一个参数可为任意值,第二个参数将被传递值一个 argumentUsed 变量。当我们想断言某参数的多个属性时,这个功能是非常有用的。

复制代码
 1     [TestMethod]
 2     public void Test_ActionsWithArgumentMatchers_PerformingActionsWithAnyArgs()
 3     {
 4       var calculator = Substitute.For<ICalculator>();
 5 
 6       var firstArgsBeingMultiplied = new List<int>();
 7       calculator.Multiply(Arg.Do<int>(x => firstArgsBeingMultiplied.Add(x)), 10);
 8 
 9       calculator.Multiply(2, 10);
10       calculator.Multiply(5, 10);
11 
12       // 由于第二个参数不为10,所以不会被 Arg.Do 匹配
13       calculator.Multiply(7, 4567); 
14 
15       CollectionAssert.AreEqual(firstArgsBeingMultiplied, new[] { 2, 5 });
16     }
复制代码

在此例中,当调用 Multiply 方法第二个参数是 10 时,不管第一个 int 类型的参数的值是多少,Arg.Do 都会将其添加到一个列表当中。

参数操作和调用指定参数

就像 Arg.Any<T>() 参数匹配器一样,当参数的类型为 T 时,参数操作会调用一个指定操作(所以也可以用于设置返回值检查接收到的调用)。它只是多了个能与匹配指定规格的调用的参数交互的功能。

复制代码
 1     [TestMethod]
 2     public void Test_ActionsWithArgumentMatchers_ArgActionsCallSpec()
 3     {
 4       var calculator = Substitute.For<ICalculator>();
 5 
 6       var numberOfCallsWhereFirstArgIsLessThan0 = 0;
 7 
 8       // 指定调用参数:
 9       // 第一个参数小于0
10       // 第二个参数可以为任意的int类型值
11       // 当此满足此规格时,为计数器加1。
12       calculator
13         .Multiply(
14           Arg.Is<int>(x => x < 0),
15           Arg.Do<int>(x => numberOfCallsWhereFirstArgIsLessThan0++)
16         ).Returns(123);
17 
18       var results = new[] {
19         calculator.Multiply(-4, 3),
20         calculator.Multiply(-27, 88),
21         calculator.Multiply(-7, 8),
22         calculator.Multiply(123, 2) // 第一个参数大于0,所以不会被匹配
23       };
24 
25       Assert.AreEqual(3, numberOfCallsWhereFirstArgIsLessThan0); // 4个调用中有3个匹配上
26       CollectionAssert.AreEqual(results, new[] { 123, 123, 123, 0 }); // 最后一个未匹配
27     }
复制代码

NSubstitute 完全手册






本文转自匠心十年博客园博客,原文链接:http://www.cnblogs.com/gaochundong/archive/2013/05/22/nsubstitute_actions_with_argument_matchers.html,如需转载请自行联系原作者
目录
相关文章
|
缓存 Windows
Windows程序设计——LoadImage参数及其用法
Windows程序设计——LoadImage参数及其用法
400 0
|
6月前
|
缓存 BI Linux
《Linux操作系统编程》第九章 数据查找和筛选工具 : 了解流编辑器sed和报表生成器awk的简单使用
《Linux操作系统编程》第九章 数据查找和筛选工具 : 了解流编辑器sed和报表生成器awk的简单使用
51 0
|
7月前
|
存储 JSON BI
一步步创建包含自定义 Screen 的 ABAP 程序的详细步骤试读版
一步步创建包含自定义 Screen 的 ABAP 程序的详细步骤试读版
45 0
|
8月前
|
Web App开发 JavaScript 前端开发
js调试的作用及用法
js调试的作用及用法
67 1
|
JSON jenkins 持续交付
python接口自动化(十六)--参数关联接口后传(详解)
大家对前边的自动化新建任务之后,接着对这个新建任务操作了解之后,希望带小伙伴进一步巩固胜利的果实,夯实基础。因此再在沙场实例演练一下博客园的相关接口。我们用自动化发随笔之后,要想接着对这篇随笔操作,不用说就需 要用参数关联了,发随笔之后会有一个随笔的 id,获取到这个 id,继续操作传这个随笔 id 就可以了(博客园的登录机制已经变了,不能用账号和密码登录了,这里用 cookie 登录)
176 1
python接口自动化(十六)--参数关联接口后传(详解)
|
搜索推荐 JavaScript 前端开发
python接口自动化(十八)--重定向(Location)(详解)
在实际工作中,有些接口请求完以后会重定向到别的url,而你却需要重定向前的url。URL主要是针对虚拟空间而言,因为不是自己独立管理的服务器,所以无法正常进行常规的操作。但是自己又不希望通过主域名的二级目录进行访问,而 是希望通过主域名的二级域名进行访问。所以这个时候就会用到URL重定向。
179 0
python接口自动化(十八)--重定向(Location)(详解)
|
前端开发 开发者
评论列表案例-演示艾特符号替代相对路径的好处|学习笔记
快速学习评论列表案例-演示艾特符号替代相对路径的好处
439 0
WebApi入门第十一章(定时器作用及语法)
WebApi入门第十一章(定时器作用及语法)
228 0
WebApi入门第十一章(定时器作用及语法)
|
Shell C#
VB实现SHELL扩展之接口参数获取失败探析
前几天有位网友问我用VB实现SHELL扩展的问题,这个问题比较有意思,虽然VB较少使用了,但是用VB开发COM组件还是比较方便的(前几天用EVC开发COM组件,相比起来,用VB还是比较幸福的),所以便进行了深入的研究。
650 0

热门文章

最新文章