Day 18-隔离框架 (isolation Framework) - NSubstitute 基本介绍-3 (核心技术-10)

NSubstitute 基本语法前言-2

今天会是基本介绍 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);
}

NSubstitute 基本语法-6:DidNotReceive

在昨天我们一起认识了 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));
}

NSubstitute 基本语法-7:ReturnsForAnyArgs()

很多时候在撰写验证商业逻辑的测试时,其实引用的第三方套件我们不太在意其中间流程,只在乎最终结果(如回传值)是符合我们预期的就好;此时,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 (预设值运算式) 做处理。


NSubstitute 基本语法-8:AndDoes()

在谈论 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);
}

NSubstitute 基本语法-9:When.....Do

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

NSubstitute 基本语法-10:Callback builder for more complex callbacks

倘若我们要在 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++"));
}

NSubstitute 基本语法-11:Throwing Exception

那今天最後要提到的就是如何撰写例外处理,搭配有回传值与无回传值的方法提供两种写法,示范的程序码如下:

[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:又是一个很深的大坑/images/emoticon/emoticon06.gif


<<:  1.2 Design System - 做的优缺点

>>:  Day03:提升工程师产能的秘诀

D30 - 舌尖上的 JS 完食!

完食文 「 恭喜您!您的铁人系列文「舌尖上的 JS」成功完赛。铁人链成不易,坚持到最後挑战成功实在太...

Kotlin Android 第30天,从 0 到 ML - 总结

这次参赛主轴分为三大部份: Kotlin Android Jetpack Tensorflower ...

[Day27 ] Chained Exploits - 链式漏洞利用(漏洞利用组合技)

前言 前面介绍了很多Web攻击,今天来讲讲组合技吧 正文 在真实世界(real-world)的恶意攻...

学习成为人体 PE Parser

看日常分享: AwesomeCS FB 看技术文章: AwesomeCS Wiki 笔者最近在阅读...

冒险村11 - frozen_string_literal

11 - frozen_string_literal 延续 Begin from linter : ...