Search code examples
c#microsoft-fakes

MSFakes, how do I stub this call twice?


I have an interface like this:

IDbSession
{
    IEnumerable<T> Query<T>(string sql, object param = null);
}

MSFakes generates an object that looks like this:

class StubIDbSession : StubBase<IDbSession>, IDbSession, IDisposable
{
    public void QueryOf1StringObject<T>(FakesDelegates.Func<string, object, System.Collections.Generic.IEnumerable<T>> stub);
}

Which I then use like this:

var session = new StubIDbSession();
session.QueryOf1StringObject((sql1, param1) =>
{
    // Stub the second call
    session.QueryOf1StringObject((sql2, param2) => new List<MyDto>());

    return myDtos.Take(1);
});

However, when stubbing the call a second time, it throws this exception:

ArgumentException: An item with the same key has already been added.

   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at Microsoft.QualityTools.Testing.Fakes.Stubs.StubDelegateMap.Concat(StubDelegateMap dictionary, Delegate delegate)
   ...

How can I stub the second call to this method??


Solution

  • It looks like MSFakes builds a dictionary internally with the return type as the key, and the Func delegate as the value. Unfortunately, it doesn't let you change the delegate after you've set it once.

    So something like this is ok:

    var session = new StubIDbSession();
    session.QueryOf1StringObject((sql1, param1) =>
    {
        return new List<string>();
    });
    session.QueryOf1StringObject((sql1, param1) =>
    {
        return new List<int>();
    });
    

    But this will throw an exception because it's trying to use the same return type twice:

    var session = new StubIDbSession();
    session.QueryOf1StringObject((sql1, param1) =>
    {
        return new List<string> { "STRING 1" }
    });
    session.QueryOf1StringObject((sql1, param1) =>
    {
        return new List<string> { "STRING 2" }
    });
    

    Here is how I worked around it:

    bool called = false;
    session.QueryOf1StringObject((sql1, param1) =>
    {
        if(called)
        {
            return new List<string> { "STRING 2" };
        }
        else
        {
            called = true;
            return new List<string> { "STRING 1" }
        }
    });
    

    Good luck if you need to call a method 3 times or more.