FakeItEasy and EntityFramework

We needed to fake or mock out Entity Framework so that we could test our “service layer” that holds our business logic without hitting a real database. We are using EF as our Repository and skipping all the extra work in creating a repository code layer that only wraps EF. We are ok with being this closely tied to EF.

It was difficult to figure out how to fake the context with an interface we made ourselves. We found some helpful Nuget packages, so I decided to share it.

EntityFramework.Testing:

EntityFramework Testing

EntityFramework.Testing.FakeItEasy:

EntityFrameworkTesting.FakeItEasy NuGet package

Looking at the project site for EntityFramework.Testing, they have some sample code for FakeItEasy as well as other mocking frameworks.

EntityFramework.Testing Project site

EntityFramework.Testing.FakeItEasy provides a helpful extension method to mock EntityFramework's DbSets using FakeItEasy.

This also supports Moq and other libraries.

Sample code using it:

using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Linq; using Acme.Data; using Acme.Models; using FakeItEasy; using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Acme.Services.Tests { [TestClass] public class ProductServiceTests { [TestMethod] [TestCategory("Product Service Get")] public void Get_All_ReturnsExpected() { // Create test data var testData = new List { new Product {ProductId = 1, Name = "Product 1", Description = "this is a description", Active = true}, new Product {ProductId = 2, Name = "Product 2", Description = "this is a description", Active = true}, new Product {ProductId = 3, Name = "Product 3", Description = "this is a description", Active = false}, new Product {ProductId = 4, Name = "Product 4", Description = "this is a description", Active = true}, };

        // Arrange
        var set = A.Fake<DbSet<Product>>(o => o.Implements(typeof(IQueryable<Product>)).Implements(typeof(IDbAsyncEnumerable<Product>)))
                    .SetupData(testData);

        var context = A.Fake<AcmeContext>();
        A.CallTo(() => context.Products).Returns(set);

        var productService = new ProductService(context);

        // Act
        var products = productService.GetAll().ToList();

        // Assert
        Assert.AreEqual(4, products.Count(), "Should have 4");
        Assert.AreEqual(1, products.First().ProductId, "Should be 1");
    }

    \[TestMethod\]
    \[TestCategory("Product Service Get")\]
    public void Get\_Active\_OnlyReturnsActive()
    {
        // Create test data
        var testData = new List<Product>
        {
            new Product {ProductId = 1, Name = "Product 1", Description = "this is a description", Active = true},
            new Product {ProductId = 2, Name = "Product 2", Description = "this is a description", Active = true},
            new Product {ProductId = 3, Name = "Product 3", Description = "this is a description", Active = false},
            new Product {ProductId = 4, Name = "Product 4", Description = "this is a description", Active = true},
        };

        // Arrange
        var set = A.Fake<DbSet<Product>>(o => o.Implements(typeof(IQueryable<Product>)).Implements(typeof(IDbAsyncEnumerable<Product>)))
                    .SetupData(testData);

        var context = A.Fake<AcmeContext>();
        A.CallTo(() => context.Products).Returns(set);

        var productService = new ProductService(context);

        // Act
        var products = productService.GetActiveProducts().ToList();

        // Assert
        Assert.AreEqual(3, products.Count(), "Should have 3");
        Assert.AreEqual(4, products.Last().ProductId, "Should be 4");
        Assert.IsFalse(products.Any(x =>x.Active == false), "None should be active");
    }
}

}

Here’s an alternative a co-worker had created before the NuGet package was discovered.

The test:

[TestMethod] [TestCategory("Product Service Get")] public void Get_All_ReturnsExpected() { // Arrange var contextFaker = new ContextFaker(); contextFaker.Products.AddRange(new List { new Product { ProductId = 1, Name="Product 1", Description = "this is a description", Active = true}, new Product { ProductId = 2, Name="Product 2", Description = "this is a description", Active = true}, new Product { ProductId = 3, Name="Product 3", Description = "this is a description", Active = false}, new Product { ProductId = 4, Name="Product 4", Description = "this is a description", Active = true}, })); var productService = new ProductService(contextFaker.FakeContext);

// Act
var products = productService.GetAll().ToList();

// Assert
Assert.AreEqual(4, products.Count(), "Should have 4");
Assert.AreEqual(1, products.First().ProductId, "Should be 1");

}

The helper code:

public class ContextFaker { public List Products = new List();

private IAcmeContext \_fakeContext;

public IAcmeContext FakeContext
{
    get
    {
        A.CallTo(() => \_fakeContext.Products).Returns(ListFaker<Product>.GetFake(Products));
        return \_fakeContext;
    }
    set { \_fakeContext = value; }
}

public ContextFaker()
{
    \_fakeContext = A.Fake<IAcmeContext>();
}

}

public static class ListFaker where T : class { public static DbSet GetFake(List data) { var dataAsQueryable = data.AsQueryable(); var fakeDbSet = A.Fake<DbSet>(b => b.Implements(typeof(IQueryable))); A.CallTo(() => ((IQueryable)fakeDbSet).GetEnumerator()).Returns(dataAsQueryable.GetEnumerator()); A.CallTo(() => ((IQueryable)fakeDbSet).Provider).Returns(dataAsQueryable.Provider); A.CallTo(() => ((IQueryable)fakeDbSet).Expression).Returns(dataAsQueryable.Expression); A.CallTo(() => ((IQueryable)fakeDbSet).ElementType).Returns(dataAsQueryable.ElementType); return fakeDbSet; } }

This article is part of the GWB Archives. Original Author: Aligned

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.