NSubstitute完全手册(十一)参数匹配器
参数匹配器可以用于设置返回值和检查接收到的调用。它提供了一种指定一个调用或一组调用的方式,这样可以对所有匹配的调用设置一个返回值,或者检查是否接收到了匹配的调用。
忽略参数
通过使用 Arg.Any<T>() 可以忽略一个T类型的参数。
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_ArgumentMatchers_IgnoringArguments() 9 { 10 var calculator = Substitute.For<ICalculator>(); 11 12 calculator.Add(Arg.Any<int>(), 5).Returns(7); 13 14 Assert.AreEqual(7, calculator.Add(42, 5)); 15 Assert.AreEqual(7, calculator.Add(123, 5)); 16 Assert.AreNotEqual(7, calculator.Add(1, 7)); 17 }
在这个例子中,我们设定当任意数与 5 相加时,返回值 7。我们使用 Arg.Any<int>() 来告诉 NSubstitute 忽略第一个参数。
我们也可以通过这种方法来匹配任意的子类型。
1 public interface IFormatter 2 { 3 void Format(object o); 4 } 5 6 [TestMethod] 7 public void Test_ArgumentMatchers_MatchSubTypes() 8 { 9 IFormatter formatter = Substitute.For<IFormatter>(); 10 11 formatter.Format(new object()); 12 formatter.Format("some string"); 13 14 formatter.Received().Format(Arg.Any<object>()); 15 formatter.Received().Format(Arg.Any<string>()); 16 formatter.DidNotReceive().Format(Arg.Any<int>()); 17 }
参数条件匹配
通过使用 Arg.Is<T>(Predicate<T> condition) 来对一个T类型的参数进行条件匹配。
1 [TestMethod] 2 public void Test_ArgumentMatchers_ConditionallyMatching() 3 { 4 var calculator = Substitute.For<ICalculator>(); 5 6 calculator.Add(1, -10); 7 8 // 检查接收到第一个参数为1,第二个参数小于0的调用 9 calculator.Received().Add(1, Arg.Is<int>(x => x < 0)); 10 // 检查接收到第一个参数为1,第二个参数为 -2、-5和-10中的某个数的调用 11 calculator 12 .Received() 13 .Add(1, Arg.Is<int>(x => new[] { -2, -5, -10 }.Contains(x))); 14 // 检查未接收到第一个参数大于10,第二个参数为-10的调用 15 calculator.DidNotReceive().Add(Arg.Is<int>(x => x > 10), -10); 16 }
如果某参数的条件表达式抛出异常,则将假设该参数未被匹配,异常本身会被隐藏。
1 [TestMethod] 2 public void Test_ArgumentMatchers_ConditionallyMatchingThrowException() 3 { 4 IFormatter formatter = Substitute.For<IFormatter>(); 5 6 formatter.Format(Arg.Is<string>(x => x.Length <= 10)).Returns("matched"); 7 8 Assert.AreEqual("matched", formatter.Format("short")); 9 Assert.AreNotEqual("matched", formatter.Format("not matched, too long")); 10 11 // 此处将不会匹配,因为在尝试访问 null 的 Length 属性时会抛出异常, 12 // 而 NSubstitute 会假设其为不匹配并隐藏掉异常。 13 Assert.AreNotEqual("matched", formatter.Format(null)); 14 }
匹配指定的参数
使用 Arg.Is<T>(T value) 可以匹配指定的T类型参数。
1 [TestMethod] 2 public void Test_ArgumentMatchers_MatchingSpecificArgument() 3 { 4 var calculator = Substitute.For<ICalculator>(); 5 6 calculator.Add(0, 42); 7 8 // 这里可能不工作,NSubstitute 在这种情况下无法确定在哪个参数上应用匹配器 9 //calculator.Received().Add(0, Arg.Any<int>()); 10 11 calculator.Received().Add(Arg.Is(0), Arg.Any<int>()); 12 }
通常来讲,这个匹配器不是必须的;大部分情况下,我们可以使用 0 来代替 Arg.Is(0)。然而在某些情况下,NSubstitute 无法解析出那个匹配器应用到了那个参数上(实际上,参数匹配器进行的是模糊匹配;而不是直接解析函数的调用)。在这些情况下会抛出一个 AmbiguousArgumentsException,并且会要求你指定一个或多个额外的参数匹配器。大多数情况下你可能不得不为每个参数显式的使用参数匹配器。