在FakeItEasy伪造一名调查员

时间:2011-03-02 17:03:52

标签: unit-testing tdd mocking fakeiteasy

如何使用FakeItEasy创建假设,允许连续调用的返回值不同。这是我希望能够做到的一个例子:

var enumerator = A.Fake<IDictionaryEnumerator>();
A.CallTo(() => enumerator.MoveNext()).Returns(true);  //Expected value for first call
A.CallTo(() => enumerator.Key).Returns("key1");
A.CallTo(() => enumerator.Value).Returns("value1");
A.CallTo(() => enumerator.MoveNext()).Returns(false); //Expected value for second call

Assert.IsTrue(enumerator.MoveNext());    //Fails
Assert.IsFalse(enumerator.MoveNext());   

断言将失败,因为它最后一次设置MoveNext将覆盖第一个。

是否可以在FakeItEasy中执行我想要的操作?

[编辑]
澄清了原始问题的例子并在下面提供了一个工作示例。

根据Patrik的回答,此代码显示了如何设置假货。诀窍是反转所有设置并使用Once()

var enumerator = A.Fake<IDictionaryEnumerator>();
A.CallTo(() => enumerator.MoveNext()).Returns(false).Once();
A.CallTo(() => enumerator.MoveNext()).Returns(true).NumberOfTimes(2);
A.CallTo(() => enumerator.Key).Returns("key2").Once();
A.CallTo(() => enumerator.Value).Returns("value2").Once();
A.CallTo(() => enumerator.Key).Returns("key1").Once();
A.CallTo(() => enumerator.Value).Returns("value1").Once();

while(enumerator.MoveNext())
{
    Debug.WriteLine(enumerator.Key + ": "+ enumerator.Value);               
}

这将打印:

key1: value1
key2: value2

2 个答案:

答案 0 :(得分:6)

我不完全确定我理解你的意思,你提供的代码总是会失败。但是,如果你的意思是你希望它返回真实的第二时间,那么它就可以完成。我可以想到几种不同的方式,其中两种是:

A.CallTo(() => enumerator.MoveNext()).ReturnsNextFromSequence(false, true);

另一种方式是:

A.CallTo(() => enumerator.MoveNext()).Returns(true);
A.CallTo(() => enumerator.MoveNext()).Returns(false).Once();

修改

第二,虽然我想我更了解你的问题,你想要发生的是MoveNext第一次应该返回true而第二次是false吗?如果是这种情况,只需更改上述示例中的值的顺序。

FakeItEasy不使用记录/重播模型,并且您是正确的,因为最新配置的规则优先于任何先前指定的规则。这就是为什么你必须指定repeat - “.Once()” - 关于它的最新配置才有效一次。

最新优先级有很多原因,其中最重要的一个原因是它允许您在灯具设置中设置默认返回值并覆盖它以在某些测试中返回特定值,使用记录/重播模型时是不可能的。

答案 1 :(得分:0)

OP的例子基于Patrik的答案很好......但是如果序列变大则很乏味。为了补充这个答案,请考虑尽管假冒/模拟示例通常会显示一堆直线代码来安排自己,但实际上您可以掌握编程语言的全部功能。条件,循环,甚至程序。

请考虑以下事项:

public static void AFakeDictionaryEnumeratorReturns(
                       IDictionaryEnumerator enumerator, params object[] pairs)
{
    if (0 != pairs.Length % 2)
        throw new ArgumentException("pairs must have even number of elements", "pairs");

    int n = pairs.Length / 2;

    A.CallTo(() => enumerator.MoveNext()).Returns(false).Once();
    A.CallTo(() => enumerator.MoveNext()).Returns(true).NumberOfTimes(n);

    for (int i = pairs.Length; i > 0; i -= 2)
    {
        A.CallTo(() => enumerator.Key).Returns(pairs[i - 2]).Once();
        A.CallTo(() => enumerator.Value).Returns(pairs[i - 1]).Once();
    }
}

现在测试成为:

var enumerator = A.Fake<IDictionaryEnumerator>();
AFakeDictionaryEnumeratorReturns(enumerator, 
                                 "key1", "value1", "key2", "value2", "key3", "value3");

var keys = new List<object>();
var values = new List<object>();
while (enumerator.MoveNext())
{
    keys.Add(enumerator.Key);
    values.Add(enumerator.Value);
}

Assert.Equal(new List<object> { "key1", "key2", "key3" }, keys);
Assert.Equal(new List<object> { "value1", "value2", "value3" }, values);

(一个IDictionaryEnumerator成对交易,所以这个例子并不是显而易见的。对于标准IEnumerator<T>,一个静态通用方法可以用于一大堆不同的普查员。)