Mock上的设置未返回预期值

时间:2016-04-15 10:30:36

标签: c# unit-testing moq

以下是我遇到的问题的简化版本:

public interface IService
{
    IProvider Provider { get; }
}

public interface IProvider
{
    List<int> Numbers{ get; }
    string Text { get; }
} 

[TestMethod]
public void ServiceTest()
{
    var service = new Mock<IService>();
    var provider = new Mock<IProvider>();

    service.Setup(s => s.Provider).Returns(provider.Object);    // A
    service.Setup(s => s.Provider.Text).Returns("some text");   // B - incorrect

    // they actually meant to do this, instead of 'B'
    // provider.Setup(p => p.Text).Returns("some text"); 

    provider.Setup(p => p.Numbers).Returns(new List<int> { 1, 2, 3 });

    DummyApplicationCode(service.Object);
}

int DummyApplicationCode(IService service)
{
    // will throw, because the Provider was replaced at 'B'
    int shouldBeOne = service.Provider.Numbers.First(); 
    return shouldBeOne;
}

单元测试失败,因为在测试的应用程序代码中,被模拟的IService返回了错误的IProvider

我最终发现这条线(记住我看到的代码并不像上面那么简单)导致了它,标记为&#39; B&#39;上面,由于误解了Moq设置,其他人添加了。

我知道模拟的后续设置会覆盖以前的设置,但我没有发现此问题,因为违规行的返回是针对单独的子属性

我希望这是设计但是它让我失望,因为我没想到有人会这样做。

我的问题:自从&#39; B&#39;仅关注提供商文字的退货,为什么服务&#39;提供商&#39;属性需要替换在&#39; A&#39;?

中定义的属性

1 个答案:

答案 0 :(得分:1)

在查看来源时,这显然是有意的:

https://github.com/moq/moq4/blob/master/Source/Mock.cs

https://github.com/moq/moq4/blob/master/Source/Interceptor.cs

SetupAddCall上使用Interceptor创建“通话”。这包含以下代码块,只要我们创建非条件设置,就会删除所有先前的设置。它甚至评论过。

if (!call.IsConditional)
            {
                lock (calls)
                {
                    // if it's not a conditional call, we do
                    // all the override setups.
                    // TODO maybe add the conditionals to other
                    // record like calls to be user friendly and display
                    // somethig like: non of this calls were performed.
                    if (calls.ContainsKey(key))
                    {
                        // Remove previous from ordered calls
                        InterceptionContext.RemoveOrderedCall(calls[key]);
                    }

                    calls[key] = call;
}