对于一个属性或者方法调用的返回值,可以使用函数来返回结果。这允许我们在替代实例中嵌入更加复杂的逻辑。尽管通常来说这不是一个好的做法,但在某些情况下确实很有用。
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_ReturnFromFunction_ReturnSum() 9 { 10 var calculator = Substitute.For<ICalculator>(); 11 12 calculator 13 .Add(Arg.Any<int>(), Arg.Any<int>()) 14 .Returns(x => (int)x[0] + (int)x[1]); 15 16 Assert.AreEqual(calculator.Add(1, 1), 2); 17 Assert.AreEqual(calculator.Add(20, 30), 50); 18 Assert.AreEqual(calculator.Add(-73, 9348), 9275); 19 }
在这个示例中,我们使用参数匹配器来匹配所有对 Add() 方法的调用,使用一个 Lambda 函数来计算第一个和第二个参数的和,并将计算的结果传递给方法调用。
调用信息
为 Returns() 和 ReturnsForAnyArgs() 方法提供的函数是一个 Func<CallInfo, T> 类型,在这里,T是方法调用将要返回的值的类型,CallInfo 类型提供访问参数列表的能力。在前面的示例中,我们使用索引器 indexer 来访问参数列表。CallInfo 也提供了一个很简便的方法用于通过强类型方式来选择参数:
1 public interface IFoo 2 { 3 string Bar(int a, string b); 4 } 5 6 [TestMethod] 7 public void Test_ReturnFromFunction_CallInfo() 8 { 9 var foo = Substitute.For<IFoo>(); 10 foo.Bar(0, "").ReturnsForAnyArgs(x => "Hello " + x.Arg<string>()); 11 Assert.AreEqual("Hello World", foo.Bar(1, "World")); 12 }
在这里,x.Arg<string>() 将返回方法调用中的 string 类型的参数,而没有使用 (string)x[1] 方式。如果在方法调用中有两个 string 类型的参数,NSubstitute 将通过抛出异常的方式来告诉你无法确定具体是哪个参数。
回调
这种技术可用于在调用时访问一个回调函数:
1 [TestMethod] 2 public void Test_ReturnFromFunction_GetCallbackWhenever() 3 { 4 var calculator = Substitute.For<ICalculator>(); 5 6 var counter = 0; 7 calculator 8 .Add(0, 0) 9 .ReturnsForAnyArgs(x => 10 { 11 counter++; 12 return 0; 13 }); 14 15 calculator.Add(7, 3); 16 calculator.Add(2, 2); 17 calculator.Add(11, -3); 18 Assert.AreEqual(counter, 3); 19 }
或者也可以在 Returns() 之后通过 AndDoes() 来指定回调:
1 [TestMethod] 2 public void Test_ReturnFromFunction_UseAndDoesAfterReturns() 3 { 4 var calculator = Substitute.For<ICalculator>(); 5 6 var counter = 0; 7 calculator 8 .Add(0, 0) 9 .ReturnsForAnyArgs(x => 0) 10 .AndDoes(x => counter++); 11 12 calculator.Add(7, 3); 13 calculator.Add(2, 2); 14 Assert.AreEqual(counter, 2); 15 }