Faking an enumerator in FakeItEasy

1.6k views Asked by At

How can I create a fake with FakeItEasy that allows different return values on successive calls. This is one example of what I would like to be able to do:

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());   

The assertion will fail since it the last set up of MoveNext will overwrite the first one.

Is it possible to do what I want in FakeItEasy?

.

[Edit]
Clarified the original question's example and provided a working example below.

Based on Patrik's answer this code shows how you can set up the fake. The trick is to reverse all setups and use 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);               
}

This will print:

key1: value1
key2: value2
2

There are 2 answers

1
Patrik Hägne On BEST ANSWER

I'm not entirely sure that I understand what you mean, the code you supplied will always fail. However if you mean that you want it to return true the second time it's called it can be done. There are a couple of different ways that I can think of, two of them are:

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

The other way is:

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

Edit:

On second though I guess I understand your question better, what you want to happen is that MoveNext should return true the first time and false the second time? If that's the case just change the orders of the values in the examples above.

FakeItEasy does not use a record/replay model and you are correct in that the latest configured rule has precedence over any earlier specified rules. That's why you have to specify repeat - ".Once()" - on the latest configuration for it only to be valid once.

There are many reasons why the latest has precedence, one of the most important ones is that it lets you set up a default return value in the setup of your fixture and override it to return specific values in some of your tests, this is impossible when using a record/replay model.

0
davidbak On

The OP's example based on Patrik's answer is fine ... but tedious if the sequence grows large. To supplement that answer consider that even though fake/mock examples most often show a bunch of straight line code to Arrange themselves you actually have the full power of a programming language at your command. Conditionals, loops, and even procedures.

So consider the following:

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

and now the test becomes:

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

(An IDictionaryEnumerator deals in pairs, so this example isn't as clearly beneficial as it could be. For a standard IEnumerator<T> a single static generic method would serve for a whole bunch of different enumerators.)