2

I find that I am often faking IDbSet from Entity Framework. I typically have an interface like this:

public interface IContext : IDisposable
{
    IDbSet<Cat> Cats { get; set; }
    IDbSet<Dogs> Dogs { get; set; }
}

Which I fake like this:

IContext context = A.Fake<IContext>();
context.Cats = new FakeDbSet<Cat>();
context.Dogs = new FakeDbSet<Dogs>();

Those last two lines of code are becoming a pain.
FakeDbSet is a custom class which we always want to use instead the FakeItEasy fake.

Is there some way I can just tell FakeItEasy that anywhere it sees an IDbSet, use FakeDbSet?

Buh Buh
  • 7,443
  • 1
  • 34
  • 61
  • I know I could reflect over the fake and assign the FakeDbSet myself, but I'm hoping there is a built in way. – Buh Buh Mar 26 '15 at 15:33

1 Answers1

3

Is there some way I can just tell FakeItEasy that anywhere it sees an IDbSet, use FakeDbSet?

Not in this way, no. There are custom Dummies, whose powers are vastly improved in the upcoming 2.0 release, but currently properties do not return Dummies when they can return a fakeable type (see issue 156 for probably way too much information on this). Otherwise, you'd be all set.

Failing that, the best option really is to use reflection to look at the properties' return types and set the value accordingly.

You could use the newly-expanded IFakeConfigurator powers in the 2.0 betas as a hook to enable this behaviour, so every fake that's created would have its properties examined and the desired FakeDbSet added.

Something like this:

public class PropertiesUseFakeDbSetFakeConfigurator : FakeConfigurator<IContext>
{
    protected override void ConfigureFake(IContext fakeObject)
    {
        var fakeObjectType = fakeObject.GetType();
        var properties = fakeObjectType.GetProperties(
            BindingFlags.Public |
            BindingFlags.Instance |
            BindingFlags.GetProperty |
            BindingFlags.SetProperty);

        foreach (var propertyInfo in properties)
        {
            var propertyType = propertyInfo.PropertyType;
            if (propertyType.IsGenericType &&
                propertyType.GetGenericTypeDefinition() == typeof (IDbSet<>))
            {
                var typeInTheSet = propertyType.GetGenericArguments()[0];
                var fakeDbSetType = typeof (FakeDbSet<>).MakeGenericType(typeInTheSet);
                var fakePropertyValue = Activator.CreateInstance(fakeDbSetType);

                propertyInfo.SetValue(fakeObject, fakePropertyValue, null);
            }
        }
    }
}

would make this pass:

[Test]
public void Properties_should_be_FakeDbSets()
{
    IContext context = A.Fake<IContext>();

    Assert.That(context.Cats, Is.InstanceOf<FakeDbSet<Cat>>());
    Assert.That(context.Dogs, Is.InstanceOf<FakeDbSet<Dog>>());
}

If you have several classes like IContext in your solution, you may want to implement IFakeConfigurator directly, rather than using FakeConfigurator<T>. It requires a little more work, but provides a more sophisticated way to determine which fakes are configured. FakeConfigurator<IContext> will only configure faked IContexts.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • Wow, thanks for putting in the time to write that, as well as all your work on FakeItEasy. Keep it up! – Buh Buh Mar 26 '15 at 17:46
  • No problem. Truth be told, while I would've been happier to say, "flip this switch" or whatever, I do enjoy these kinds of... "puzzles", I guess I'll call them. Of course, this will only work if there's a getter and a setter for the property. Setterless properties would need more work to build up the proper `A.CallTo`. Not a huge deal, just slightly annoyinger. – Blair Conrad Mar 26 '15 at 18:27