• 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 完全手册

  • 相关阅读:
    线程高级应用-心得2-同步锁讲解及面试题案例分析
    线程高级应用-心得1-传统线程和定时器讲解及案例分析
    Map拷贝 关于对象深拷贝 浅拷贝的问题
    HashMap对象的深层克隆
    java Collections.sort()实现List排序自定义方法
    java中观察者模式Observable和Observer
    mysql字符串函数(转载)
    CSS的三种样式表和优先级
    Android之微信支付
    Android之扫描二维码和根据输入信息生成名片二维码
  • 原文地址:https://www.cnblogs.com/gaochundong/p/nsubstitute_auto_and_recursive_mocks.html
Copyright © 2020-2023  润新知