Search code examples
autofacfakeiteasy

Why does calling AutoFake.Provide() wipe out fakes already configured with A.CallTo()?


Why does calling fake.Provide<T>() wipe out fakes already configured with A.CallTo()? Is this a bug?

I'm trying to understand a problem I've run into with Autofac.Extras.FakeItEasy (aka AutoFake). I have a partial solution, but I don't understand why my original code doesn't work. The original code is complicated, so I've spent some time simplifying it for the purposes of this question.

Why does this test fail? (working DotNetFiddle)

public interface IStringService { string GetString(); }

public static void ACallTo_before_Provide()
{
    using (var fake = new AutoFake())
    {
        A.CallTo(() => fake.Resolve<IStringService>().GetString())
            .Returns("Test string");

        fake.Provide(new StringBuilder());
        
        var stringService = fake.Resolve<IStringService>();
        string result = stringService.GetString();

        // FAILS. The result should be "Test string",
        // but instead it's an empty string.
        Console.WriteLine($"ACallTo_before_Provide(): result = \"{result}\"");
    }
}

If I swap the order of the calls to fake.Provide<T>() and A.CallTo(), it works:

public static void Provide_before_ACallTo()
{
    // Same code as above, but with the calls to
    // fake.Provide<T>() and A.CallTo() swapped
    using (var fake = new AutoFake())
    {
        fake.Provide(new StringBuilder());
        
        A.CallTo(() => fake.Resolve<IStringService>().GetString())
            .Returns("Test string");
        
        var stringService = fake.Resolve<IStringService>();
        string result = stringService.GetString();

        // SUCCESS. The result is "Test string" as expected
        Console.WriteLine($"Provide_before_ACallTo(): result = \"{result}\"");
    }
}

I know what is happening, sort of, but I'm not sure if it's intentional behavior or if it's a bug.

What is happening is, the call to fake.Provide<T>() is causing anything configured with A.CallTo() to be lost. As long as I always call A.CallTo() after fake.Provide<T>(), everything works fine.

But I don't understand why this should be.

  • I can't find anything in the documentation stating that A.CallTo() cannot be called before Provide<T>().
  • Likewise, I can't find anything suggesting Provide<T>() cannot be used with A.CallTo().

It seems the order in which you configure unrelated dependencies shouldn't matter.

Is this a bug? Or is this the expected behavior? If this is the expected behavior, can someone explain why it works like this?


Solution

  • It isn't that the Fake's configuration is being changed. In the first test, Resolve is returning different Fakes each time it's called. (Check them for reference equality; I did.)

    Provide creates a new scope and pushes it on a stack. The topmost scope is used by Resolve when it finds an object to return. I think this is why you're getting different Fakes in ACallTo_before_Provide.

    Is this a bug? Or is this the expected behavior? If this is the expected behavior, can someone explain why it works like this?

    It's not clear to me. I'm not an Autofac user, and don't understand why an additional scope is introduced by Provide. The stacked scope behaviour was introduced in PR 18. Perhaps the author can explain why.

    In the meantime, if possible, I'd Provide all you need to before Resolveing, if you can manage it.