.Returns<T> (this T value, ... )
扩展方法如何在幕后工作?
具体来说,.Returns
如何根据执行该方法的结果知道它打算配置哪种方法?
示例:
public interface ICalculator { Add(int a, int b); }
// create mock
var calculator = Substitute.For<ICalculator>();
// How does this piece work under the hood?
calculator.Add(1, 2).Returns(3);
答案 0 :(得分:5)
每当替代者接收到一个呼叫时,它会记录有关该呼叫的信息,并更新一些全局状态(threadlocal,as Scott pointed out)记录它是最近的替代呼叫。
当.Returns
运行时,它会查找最后一个被调用的替换,然后告诉替换,它的最后一个调用应该被存根以返回该特定值。 (它还会将其从接收到的呼叫集合中删除,因此如果我们运行.Received()
,则存根呼叫不会因真实呼叫而混淆。)
calculator
.Add(1, 2) // substitute records Add(1,2) called. Last substitute
// set to `calculator`. Returns default `int` in this case.
.Returns(3) // Looks up last sub, sets its last call to return 3.
我认为这是对发生的事情的合理近似。要想要查看代码,要添加更多精度,替换为dynamic proxy forwards每次调用“call router”来处理替换的所有逻辑(存储呼叫,配置呼叫,添加回叫等)。全局状态是SubstitutionContext
,它存储接收呼叫的最后一个呼叫路由器。
(回复链接指向v4.0.0-rc1
标记。更高版本可能会发生变化,但整体想法应保持相当一致。)
答案 1 :(得分:3)
我相信它可以通过在调用模拟方法时在线程本地存储中保存上下文(称为ISubstitutionContext)来工作。然后对Return的调用会抓取此上下文并在返回对象中设置适当的数据。
模拟方法的实际实现(非常粗略地)看起来像:
//Dynamically created mock
public int Add(int a, int b)
{
var context = new SubstitutionContext("Add", ...);
//CallContext.LogicalSetData or
//ThreadStatic or
//ThreadLocal<T> or
//...
return 0;
}
//In some extension class
public static ConfiguredCall Returns<T>(this T value, ...)
{
var context = SubstitutionContext.Current; //Gets from thread local storage
return context.LastCallShouldReturn(value);
}