今天会是基本介绍 NSubstitute 的最後一个篇章 XD (含今天花了四天的篇幅介绍,还有一些比较深的写法看之後能否抽出时间补上),那今天还会介绍几个也算是常见的语法 DidNotReceive、ReturnsForAnyArgs、AndDoes、When...Do、Callback builder 与 Throwing Exceptions,商业逻辑主角跟昨天一样是官方网站提供的计算机,分别有模式 Mode 属性与 Add 方法,程序码如下:
namespace CalculatorLibrary
{
public interface ICalculator
{
string Mode { get; set; }
int Add(int a, int b);
}
}
除此之外,今天会因应模拟物件继承不会有回传值的介面,提供另一个商业逻辑程序码,如下:
public interface IFoo {
void SayHello(string to);
}
在昨天我们一起认识了 Received 方法,来确认是否有执行,细一点可以确认执行几次;那 DidNotReceive 就是 指验证是否没有接收,用 Received 的写法就是 Received(0)(接收 0 次),范例如下:
[Test]
public void DemoDidNotReceiveTest()
{
// Arrange
var calculator = Substitute.For<ICalculator>();
// Act
calculator.Add(1, 2);
calculator.Add(-100, 100);
// Arrange
calculator.DidNotReceive()
.Add(Arg.Any<int>(), Arg.Is<int>(x => x >= 500));
}
很多时候在撰写验证商业逻辑的测试时,其实引用的第三方套件我们不太在意其中间流程,只在乎最终结果(如回传值)是符合我们预期的就好;此时,ReturnsForAnyArgs 方法就可以帮助我们,这只方法是输入的方法不管是什麽,所回传的数值必然是我们所设定的期望值,其范例如下:
[Test]
public void DemoReturnsForAnyArgsTest()
{
// Arrange
var calculator = Substitute.For<ICalculator>();
calculator.Add(1, 2).ReturnsForAnyArgs(100);
// calculator.Add(default, default).ReturnsForAnyArgs(100);
// Act + Assert
Assert.AreEqual(100, calculator.Add(1, 2));
}
PS:面对 ReturnsForAnyArgs 的重点是在最後的回传值,其参数可使用 C# 提供的 default (预设值运算式) 做处理。
在谈论 AndDoes 之前,必须先了解一个概念:CallBack,而 CallBack 简单来说就是指一个程序执行完再去执行另一个程序 (参考来源:什麽是Callback函式),而 AndDoes 就是指要执行的下一只程序码,范例如下:
[Test]
public void DemoAndDoesTest()
{
// Arrange
var counter = 0;
var calculator = Substitute.For<ICalculator>();
calculator.Add(default, default)
.ReturnsForAnyArgs(x => 0)
.AndDoes(x => counter++);
// Act
calculator.Add(7, 3);
calculator.Add(2, 2);
// Assert
Assert.AreEqual(counter, 2);
}
When..Do 总共需要设定两个设定来启动 CallBack 机制;第一步,去呼叫方法(大多时候是 void 方法,但其实可以用在有回传值的方法,但不建议其理由是有回传值的方法建议用 Returns() 方法,让撰写的测试法可以区别哪些是有回传值,那些则不);其次,利用 Do() 方法启动下一个方法,示范的程序码如下:
[Test]
public void DemoWhenDoTest() {
// Arrange
var counter = 0;
var foo = Substitute.For<IFoo>();
foo.When(x => x.SayHello("World"))
.Do(x => counter++);
// Act
foo.SayHello("World");
foo.SayHello("World");
// Arrange
Assert.AreEqual(2, counter);
}
倘若我们要在 When..Do 方法里面的 Do 方法撰写一系列的 CallBack 方法们,则可以建置一个 CallBack Builder,建置完後再执行 CallBack Builder 的子方法(因篇幅关系,这边就先不探讨子方法的细节了),示范程序码如下:
[Test]
public void DemoCallbackBuilderTest() {
// Arrange
var sub = Substitute.For<ISomething>();
var calls = new List<string>();
var counter = 0;
sub.When(x => x.Something())
.Do(
Callback.First(x => calls.Add("1"))
.Then(x => calls.Add("2"))
.Then(x => calls.Add("3"))
.ThenKeepDoing(x => calls.Add("+"))
.AndAlways(x => counter++)
);
// Act
for (int i = 0; i < 5; i++)
{
sub.Something();
}
// Arrange
Assert.That(String.Concat(calls), Is.EqualTo("123++"));
}
那今天最後要提到的就是如何撰写例外处理,搭配有回传值与无回传值的方法提供两种写法,示范的程序码如下:
[Test]
public void DemoThrowingExceptionWithReturnsTest()
{
// Arrange
var calculator = Substitute.For<ICalculator>();
calculator.Add(-1, -1).Returns(x => { throw new Exception(); });
// Act + Assert
Assert.Throws<Exception>(() => calculator.Add(-1, -1));
}
[Test]
public void DemoThrowingExceptionWithWhenDoTest()
{
// Arrange
var calculator = Substitute.For<ICalculator>();
calculator.When(x => x.Add(-2, -2))
.Do(x => { throw new Exception(); });
// Act + Assert
Assert.Throws<Exception>(() => calculator.Add(-2, -2));
}
终於算把常用的 NSubstitute 语法告一个段落了,这几天介绍的语法其最终的目的就是要协助我们快速建置假物件,省下撰写假物件的时间;那接下来就要讨论单元测试必然要讨论的议题:重构(Refactoring)与接缝(Seam)。
PS:又是一个很深的大坑
完食文 「 恭喜您!您的铁人系列文「舌尖上的 JS」成功完赛。铁人链成不易,坚持到最後挑战成功实在太...
这次参赛主轴分为三大部份: Kotlin Android Jetpack Tensorflower ...
前言 前面介绍了很多Web攻击,今天来讲讲组合技吧 正文 在真实世界(real-world)的恶意攻...
看日常分享: AwesomeCS FB 看技术文章: AwesomeCS Wiki 笔者最近在阅读...
11 - frozen_string_literal 延续 Begin from linter : ...