NSubstitute完全手册(九)检查接收到的调用

简介:

在某些情况下(尤其是对void方法),检测替代实例是否能成功接收到一个特定的调用是非常有用的。可以通过使用 Received() 扩展方法,并紧跟着被检测的方法。

复制代码
 1     public interface ICommand
 2     {
 3       void Execute();
 4       event EventHandler Executed;
 5     }
 6 
 7     public class SomethingThatNeedsACommand
 8     {
 9       ICommand command;
10       public SomethingThatNeedsACommand(ICommand command)
11       {
12         this.command = command;
13       }
14       public void DoSomething() { command.Execute(); }
15       public void DontDoAnything() { }
16     }
17 
18     [TestMethod]
19     public void Test_CheckReceivedCalls_CallReceived()
20     {
21       //Arrange
22       var command = Substitute.For<ICommand>();
23       var something = new SomethingThatNeedsACommand(command);
24 
25       //Act
26       something.DoSomething();
27 
28       //Assert
29       command.Received().Execute();
30     }
复制代码

在这个例子中,command 收到了一个对 Execute() 方法的调用,所以会顺利地完成。如果没有收到对 Execute() 的调用,则 NSubstitute 会抛出一个 ReceivedCallsException 异常,并且会显示具体是在期待什么方法被调用,参数是什么,以及列出实际的方法调用和参数。

检查一个调用没有被收到

通过使用 DidNotReceive() 扩展方法,NSubstitute 可以确定一个调用未被接收到。
复制代码
 1     [TestMethod]
 2     public void Test_CheckReceivedCalls_CallDidNotReceived()
 3     {
 4       //Arrange
 5       var command = Substitute.For<ICommand>();
 6       var something = new SomethingThatNeedsACommand(command);
 7 
 8       //Act
 9       something.DontDoAnything();
10 
11       //Assert
12       command.DidNotReceive().Execute();
13     }
复制代码

检查接收到指定次数的调用

Received() 扩展方法会对某成员的至少一次的调用进行断言,DidNotReceive() 则会断言未收到调用。NSubstitute 也提供允许断言某调用是否接收到了指定的次数的选择,通过传递一个整型值给 Received() 方法。如果替代实例没有接收到给定的次数,则将会抛出异常。接收到的次数少于或多于给定次数,断言会失败。
复制代码
 1     public class CommandRepeater
 2     {
 3       ICommand command;
 4       int numberOfTimesToCall;
 5       public CommandRepeater(ICommand command, int numberOfTimesToCall)
 6       {
 7         this.command = command;
 8         this.numberOfTimesToCall = numberOfTimesToCall;
 9       }
10 
11       public void Execute()
12       {
13         for (var i = 0; i < numberOfTimesToCall; i++) command.Execute();
14       }
15     }
16 
17     [TestMethod]
18     public void Test_CheckReceivedCalls_CallReceivedNumberOfSpecifiedTimes()
19     {
20       // Arrange
21       var command = Substitute.For<ICommand>();
22       var repeater = new CommandRepeater(command, 3);
23 
24       // Act
25       repeater.Execute();
26 
27       // Assert
28       // 如果仅接收到2次或者4次,这里会失败。
29       command.Received(3).Execute();
30     }
复制代码

Received(1) 会检查该调用收到并且仅收到一次。这与默认的 Received() 不同,其检查该调用至少接收到了一次。Received(0) 的行为与 DidNotReceive() 相同。

接收到或者未接收到指定的参数

我们也可以使用参数匹配器来检查是否收到了或者未收到包含特定参数的调用。在 参数匹配器一节会介绍更多细节,下面的例子演示了一般用法:
复制代码
 1     public interface ICalculator
 2     {
 3       int Add(int a, int b);
 4       string Mode { get; set; }
 5     }
 6 
 7     [TestMethod]
 8     public void Test_CheckReceivedCalls_CallReceivedWithSpecificArguments()
 9     {
10       var calculator = Substitute.For<ICalculator>();
11 
12       calculator.Add(1, 2);
13       calculator.Add(-100, 100);
14 
15       // 检查接收到了第一个参数为任意值,第二个参数为2的调用
16       calculator.Received().Add(Arg.Any<int>(), 2);
17       // 检查接收到了第一个参数小于0,第二个参数为100的调用
18       calculator.Received().Add(Arg.Is<int>(x => x < 0), 100);
19       // 检查未接收到第一个参数为任意值,第二个参数大于等于500的调用
20       calculator
21         .DidNotReceive()
22         .Add(Arg.Any<int>(), Arg.Is<int>(x => x >= 500));
23     }
复制代码

忽略参数

就像我们可以 为任意参数设置返回值一样,NSubstitute 可以检查收到或者未收到调用,同时忽略其中包含的参数。此时我们需要使用 ReceivedWithAnyArgs() 和 DidNotReceiveWithAnyArgs()。
复制代码
 1     [TestMethod]
 2     public void Test_CheckReceivedCalls_IgnoringArguments()
 3     {
 4       var calculator = Substitute.For<ICalculator>();
 5 
 6       calculator.Add(1, 3);
 7 
 8       calculator.ReceivedWithAnyArgs().Add(1, 1);
 9       calculator.DidNotReceiveWithAnyArgs().Subtract(0, 0);
10     }
复制代码

检查对属性的调用

同样的语法可以用于检查属性的调用。通常情况下,或许我们会避免这种检测,可能我们对所需的行为进行测试更感兴趣,而不是实现细节的精确性(例如,我们可以设置一个属性返回一个值,然后检测该值是否被合理的使用,而不是断言该属性的 getter 被调用了)。当然,有些时候检查 getter 和 setter 是否被调用仍然会派的上用场,所以,这里会介绍如何使用该功能:
复制代码
 1     [TestMethod]
 2     public void Test_CheckReceivedCalls_CheckingCallsToPropeties()
 3     {
 4       var calculator = Substitute.For<ICalculator>();
 5 
 6       var mode = calculator.Mode;
 7       calculator.Mode = "TEST";
 8 
 9       // 检查接收到了对属性 getter 的调用
10       // 这里需要使用临时变量以通过编译
11       var temp = calculator.Received().Mode;
12 
13       // 检查接收到了对属性 setter 的调用,参数为"TEST"
14       calculator.Received().Mode = "TEST";
15     }
复制代码

检查调用索引器

其实索引器只是另外一个属性,所以我们可以使用相同的语法来检查索引器调用。
复制代码
1     [TestMethod]
2     public void Test_CheckReceivedCalls_CheckingCallsToIndexers()
3     {
4       var dictionary = Substitute.For<IDictionary<string, int>>();
5       dictionary["test"] = 1;
6 
7       dictionary.Received()["test"] = 1;
8       dictionary.Received()["test"] = Arg.Is<int>(x => x < 5);
9     }
复制代码

检查事件订阅

与属性一样,我们通常更赞成测试所需的行为,而非检查对特定事件的订阅。可以通过使用在替代实例上 引发一个事件的方式,并且断言我们的类在响应中执行的正确的行为:
复制代码
 1     public class CommandWatcher
 2     {
 3       ICommand command;
 4       public CommandWatcher(ICommand command)
 5       {
 6         this.command = command;
 7         this.command.Executed += OnExecuted;
 8       }
 9       public bool DidStuff { get; private set; }
10       public void OnExecuted(object o, EventArgs e) 
11       { 
12         DidStuff = true; 
13       }
14     } 
15 
16     [TestMethod]
17     public void Test_CheckReceivedCalls_CheckingEventSubscriptions()
18     {
19       var command = Substitute.For<ICommand>();
20       var watcher = new CommandWatcher(command);
21 
22       command.Executed += Raise.Event();
23 
24       Assert.IsTrue(watcher.DidStuff);
25     }
复制代码

当然,如果需要的话,Received() 会帮助我们断言订阅是否被收到:

复制代码
 1     [TestMethod]
 2     public void Test_CheckReceivedCalls_MakeSureWatcherSubscribesToCommandExecuted()
 3     {
 4       var command = Substitute.For<ICommand>();
 5       var watcher = new CommandWatcher(command);
 6 
 7       // 不推荐这种方法。
 8       // 更好的办法是测试行为而不是具体实现。
 9       command.Received().Executed += watcher.OnExecuted;
10       // 或者有可能事件处理器是不可访问的。
11       command.Received().Executed += Arg.Any<EventHandler>();
12     }
复制代码

NSubstitute 完全手册






本文转自匠心十年博客园博客,原文链接:http://www.cnblogs.com/gaochundong/archive/2013/05/22/nsubstitute_checking_received_calls.html,如需转载请自行联系原作者
目录
相关文章
|
7月前
|
Java API 调度
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
371 0
|
3月前
|
网络协议 API Windows
MASM32v11编程调用Process32First失败: 程序发出命令,但命令长度不正确
MASM32v11编程调用Process32First失败: 程序发出命令,但命令长度不正确
|
5月前
|
Python
接上python如何处理下载中断的情况
实现断点续传机制涉及记录已下载的字节数、使用`Range`头继续下载及合并文件块。Python示例代码展示了如何通过`requests`库从上次中断的位置开始下载,将新内容追加到现有文件。函数`resume_download(url, filename, last_byte=0)`接收URL、文件名和最后字节位置作为参数。注意,实际使用时需确保URL和文件路径有效。
149 0
|
6月前
|
监控 Serverless Linux
函数计算操作报错合集之安装模块时遇到错误,该如何处理
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
|
消息中间件 存储 API
事件,消息,消息处理函数,第一个图形界面程序(附带官方解释链接)
事件,消息,消息处理函数,第一个图形界面程序(附带官方解释链接)
|
Python
Python多线程下调用win32com包相关问题:pywintypes.com_error: (-2147221008, ‘尚未调用 CoInitialize。‘, None, None)问题处理
Python多线程下调用win32com包相关问题:pywintypes.com_error: (-2147221008, ‘尚未调用 CoInitialize。‘, None, None)问题处理
1151 0