【问题标题】:How does NSubstitute .Returns<T>() work?NSubstitute .Returns<T>() 如何工作?
【发布时间】:2016-09-09 11:15:59
【问题描述】:

.Returns&lt;T&gt; (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);

【问题讨论】:

标签: c# mocking nsubstitute


【解决方案1】:

每当替代品收到呼叫时,它都会记录有关该呼叫的信息,并更新一些全局状态(线程本地,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标签。以后的版本可能会改变,但总体思路应该保持相当一致。)

【讨论】:

    【解决方案2】:

    我相信,当调用模拟方法时,它可以通过在线程本地存储中保存上下文(称为 ISubstitutionContext)来工作。然后对 Returns 的调用会获取此上下文并在返回对象中设置适当的数据。

    模拟方法的实际实现(非常粗略)看起来像:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-30
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-19
      相关资源
      最近更新 更多