Save time mocking – use your real Entity Framework DbContext in unit tests

How many times have you spotted software architecture that utilizes .NET Entity Framework database context as a simple Repository pattern? Let us imagine an architecture structure like this, from top to bottom:

  • Database for data persisting
  • Entity Framework DbContext as Repository pattern
  • Service layer that uses DbContext for data operations
  • Business models (not related to database) that the Service layer can return or operate
  • UI layer

The problem

How many times have you wondered how to perform unit tests in such an application service layer?
Entity Framework DbContext looks simple to mock by great tools like Moq [https://github.com/Moq/moq4/wiki/Quickstart]. Using such a tool to create a mock based on an interface is rather simple. But keep in mind that under the hood EF has mechanics for many things, like: generating ids, entity state tracking, virtual collection loading etc. Those cannot be quickly mocked, and from the unit test perspective it is worthless to spend a lot of time on mocking rather than on testing.

On one hand the perfect solution will be to refactor and introduce a pure Repository pattern layer. It would implement an interface known for the Service layer and encapsulate DBContext internally. Such a repository could be injected to the Service layer and mocked on purpose. But on the other hand it needs serious changes in the whole application architecture, which is very often not acceptable.

Based on that we know that mocking DBContext is tricky if adding a new layer requires serious changes. How to deal with such a situation? The solution could be to mock the database.

Effort-less?

Effort [https://effort.codeplex.com/] is an acronym of Entity Framework Fake ObjectContext Realization Tool. Obviously it is also able to work with DbContext objects.

How does it work?
It emulates the relational database server with a completely .NET based lightweight in-memory database. All the data operations are redirected to it, so the tests can run completely in-process.

Limitations?
It would not work with pure SQL commands, things like ExecuteStoreCommand, ADO.NET 😉 Anyway when using ORM you should also forget about these.

Test application

For testing purposes lets imagine a Shop application that has:

  • SimpleShop.DataLayer (DbContext and Entity models)
  • SimpleShop.ServiceLayer (Logic and business models)
  • SimpleShop.ServiceLayer.Tests (unit tests)

DataLayer

We have got a number of Products in Categories:

namespace SimpleShop.DataLayer
{
    public class Category
    {
        public Category()
        {
            Products = new HashSet<Product>();
        }

        public int Id { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Product> Products { get; set; }
    }
}
namespace SimpleShop.DataLayer
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }

        public virtual Category Category { get; set; }
    }
}

And the DbContext implementation:

namespace SimpleShop.DataLayer
{
    public class ShopDataContext : DbContext
    {
        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
    }
}

ServiceLayer

The Services layer contains only one service that returns a list of Product business models:

namespace SimpleShop
{
    public class ProductModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }
        public string CategoryName { get; set; }
    }
}

The method here is covering the following business case “If a product starts with the letter P then apply a special discount to it”.

public class ProductService
    {
        public List<ProductModel> Get(string category)
        {
            using (var context = new ShopDataContext())
            {
                return context.Products
                    .Where(x => x.Category.Name == category)
                    .Select(x => 
                        new ProductModel 
                        {  
                            Id = x.Id,
                            CategoryName = x.Category.Name,
                            Price = x.Name.StartsWith("P") ? x.Price * 0.5 : x.Price,
                            Name = x.Name
                        })
                    .ToList();
            }
        }
    }

Setup
First we need to install Effort itself. Add Effort.EF6 package from NuGet to your unit tests project.

Save your time_effort

We need IDBConnectionFactory implementation, because we do not want to modify DBContext itself. So instead we will configure EF to use our factory when a default connection is created.

namespace SimpleShop.ServiceLayer.Tests
{
    public class EffortProviderFactory : IDbConnectionFactory
    {
        private static DbConnection _connection;
        private readonly static object _lock = new object();

        public static void ResetDb()
        {
            lock (_lock)
            {
                _connection = null;
            }
        }

        public DbConnection CreateConnection(string nameOrConnectionString)
        {
            lock (_lock)
            {
                if (_connection == null)
                {
                    _connection = Effort.DbConnectionFactory.CreateTransient();
                }

                return _connection;
            }
        }
    }
}

EffortProviderFactory is thread-safe-wise, and it captures the DbConnection inside. The static field handles one instance of DbConnction that is returned every time Entity Framework ask for it. The ResetDb() method is used before every test to dispose of the previous connection.

For a different scenario you can also use a method which creates a persistent database:

Effort.DbConnectionFactory.CreatePersistant(string databaseId);

It will always return the connection to the same database (based on the database parameter). But in our case it would be hard to clear the database during every test.

Note that System.Data.Common is needed here so add it from framework references:

Save your time_system data

Now we need to tell EF that a new provider should be used. In App.config (for unit test project):

<defaultConnectionFactory type="SimpleShop.ServiceLayer.Tests.EffortProviderFactory, SimpleShop.ServiceLayer.Tests">
    </defaultConnectionFactory>

The last step is little tricky, but there is a need to override the default EF provider setting per assembly. You can add a unit test class without any unit tests but with an assembly initializer like (this code will be run once per whole unit tests project):

[TestClass]
    public class TestsInitialize
    {
        [AssemblyInitialize]
        public static void AssemblyInit(TestContext context)
        {
            Effort.Provider.EffortProviderConfiguration.RegisterProvider();
        }
    }

Just start testing!

Now you can just start writing your unit tests without thinking about mocking DbContext. Once you need some data in the database simply use Entity Framework to manipulate your data. Do not worry about ids, keys, or virtual properties – everything will work in exactly the same way as with Entity Framework and an ordinary database.

Also remember to purge your Effort connection database when needed. I do it in TestInitialize method, which is run before every unit test in test class.

[TestInitialize]
        public void MyTestInitialize()
        {
            EffortProviderFactory.ResetDb();
        }

Let us write some examples. First, some data preparation:

private void PrepareData()
        {
            using (var model = new ShopDataContext())
            {
                var category = new Category { Name = "tools" };
                var otherCategory = new Category { Name = "food" };

                category.Products.Add(new Product() 
                { Name = "Notepad", Category = category, Price = 10.0 });
                category.Products.Add(new Product() 
                { Name = "Pencil", Category = category, Price = 4.0 });
                category.Products.Add(new Product() 
                { Name = "Pen", Category = category, Price = 6.0 });
                otherCategory.Products.Add(new Product() 
                { Name = "Pear", Category = otherCategory, Price = 2.0 });

                model.Categories.Add(category);
                model.Categories.Add(otherCategory);

                model.SaveChanges();
            }
        }

Something simple at the beginning, check if products are in proper categories (testing if Service using argument to separate categories):

[TestMethod]
        public void Get_ShouldReturnNumberOfProductsForOneCategory()
        {
            PrepareData();

            var productService = new ProductService();
            var result = productService.Get("tools");

            Assert.AreEqual(3, result.Count);
            Assert.IsTrue(result.All(x => x.CategoryName == "tools"));
        }

Then check if there is only one Pear in the Food category:

[TestMethod]
        public void Get_ShouldReturnOnlyProductFromCategory()
        {
            PrepareData();

            var productService = new ProductService();
            var result = productService.Get("food");

            Assert.AreEqual(1, result.Count);
            Assert.AreEqual("Pear", result.First().Name);
        }

Let us test now what we really should test: business logic!

Working with Discount logic, first check if Discount applies to products that start with the letter P (the original price for Pencil is 4.0, so it is expected that the price returned from Service is 2.0, the same rule for Pen, original price is 6.0, but 3.0 should be returned):

[TestMethod]
        public void Get_ShouldReturnSpecialPriceForProductsStartingWithP()
        {
            PrepareData();

            var productService = new ProductService();
            var result = productService.Get("tools");

            Assert.AreEqual(2.0, result.First(x => x.Name == "Pencil").Price);
            Assert.AreEqual(3.0, result.First(x => x.Name == "Pen").Price);
        }

To make sure the rule applies only to some products check if Notepad still has the regular price:

[TestMethod]
        public void Get_ShouldReturnNormalPriceForOtherProducts()
        {
            PrepareData();

            var productService = new ProductService();
            var result = productService.Get("tools");

            Assert.AreEqual(10.0, result.First(x => x.Name == "Notepad").Price);
        }

Happy testing!

22 comments

  1. When Effort evalutes lambda queries, does it emulate all the nuances of a DBContext vs. an in-memory object collection? For example, if I have a SalesRep table with a one-to-many relationship with a SalesOrder table, and I want get SalesRep records where(sr => !sr.SalesOrder.Any()), I would need to make sure the SalesOrder collection is not null for my in-memory mock database (or I receive a null reference exception), whereas EF treats the null case as a zero-length collection.

    Also, there are queries I can run successfully against an in-memory database in a unit test that will fail to run in EF because it does not know how to translate them into SQL. I would hope that Effort would make those queries fail as well.

  2. @Kerry Patrick
    Effort works underneath EF in that replaces the database server. So it’s still EF doing the SQL query conversion and applying its semantics of empty collections, etc. That’s the nice thing about Effort – your EF code does what it was already doing, just it’s against an in-memory “SQL Server” that you can initialise from scratch every test run (or load in from a file) instead of manipulating an external database and all of the headaches for testability that brings.

  3. @mateusz.roszczak
    You have presented a fine framework that allows to deal with a problem that should not exist in the first place. It is a common mistake that developers tend to tightly couple service layer and data access layer. On the other hand when dealing with legacy systems one sometimes may need to put tests around “untestable” code.

    However to be perfectly honest I believe that you are missing the point of unit tests. The usual definition is to:
    1) Take the smallest piece of testable software in the application,
    2) Isolate it from the remainder of the code,
    3) Determine whether it behaves exactly as you expect,
    And from above requrements you have met only the third one.

    But more importantly unit tests serve as indicators of any “code smell”. And the need to create in-memory “SQL Server” to unit test single method in service layer is a pretty huge “code smell”.

    So to sum up: it might be possible to use “Effort” during: integration, functional, smoke or other high level tests, as it provides almost flawless DB mock for an ORM. It may aslo be useful for unit testing data access layer. But please consider not using it for unit tests of service layer (unless you work in legacy system that is totally not fit for refactoring).

    P.S. Thank you for sharing information about “Effort”.
    P.S.2 Your implementaion of ProductService hides its dependency on ShopDataContext which is also a “code smell”.

    1. Thanks a lot for your detailed reply!
      I’m encouraging to separate concepts, layers and responsibilities too.
      Proper way to handle data layer in newly designed application is Repository Pattern which is provided (injected) as dependency.
      I agree that “Effort” is rather for integration (maybe functional high level), but also find that this kind of tools are not often known for developers. I saw people that try to Mock whole DbContext logic just to separate some other code in just to test something outside the project. So in my opinion showing that such tools exist can save somebody time 🙂

      Best Regards!

    2. Whilst you raise some excellent points effort and effort.ef6 definitely has a place. For those that don’t want to maintain an in memory database for full end to end tests effort presents a way to conduct that sort of testing enabling the public api to be called as a customer would and then mapping layers etc to execute to weed out any potential errors that may happen when data is populated. I agree with almost everything else you have said. Your unit tests should exist in isolation to those sorts of tests. Yes if you HAVE to create an in memory database this is a code smell, because you should be able to mock out your boundary correctly using other techniques. The fact you can and have end to end tests with a repeatable and controllable in memory structure is a definite advantage.

    3. I might be wrong, but… I get a feeling that when you go too far with unit tests isolation concept (” Take the smallest piece of testable software in the application”), you are reducing the usefulness of your unit tests – or have to create too many.

      I mean, your code should be split into small chunks with one purpose only for each of them. A class that does ‘one’ bit is often very simple – not only to unit test, but in general – hard to create a bug in it.

      So, lets say that a webapi controller action uses a few of the simple bits – it is here that things can go wrong – the bits might be called incorrectly, their execution order might be changed or something else might happen in the lifecycle of the project. Unit tests that test the ‘smallest testable piece’ do not guard against that, do they?

      So, it makes sense to me to have a unit test for a larger piece of code, which performs some actual end-to-end function – for example, a test for ‘UpdateUserData’ action of a UserController.
      That action might for example use a UserProvider thing that gets the user based on ID, UserDataValidator that validates whether the data submitted is proper and then return a result that contains the details of the updated user entity.
      This can get broken in many ways, even though each of the functionalities might still work OK.

      In such case, a controller test method could see if proper result is sent if user is not found, or when everything goes wrong, you can assert that ‘repository.Save();’ method was called etc etc.

      And, in any scenario that gets a little bit more advanced, where db context is needed for more than just “get single user from repository”, you need to mock quite a lot of stuff. And mocking is time consuming, sometimes pretty complicated (when you have to mock callbacks etc)… Soo, seems that this Effort thing might be pretty cool…

      Nice article Mateusz, thanks:)

  4. I am using this code, and it is still using the real database to insert values when I call the save changes in the prepare data section. Am I supposed to inject the Effort connection somewhere, because it seems to still be using the real connection from the connection string in my unit test.

    1. For future visitors with this issue, you need to go into your app.config, copy the connection string in the element, and then paste that as an argument to CreateTransient in your new EffortProviderFactory, and you also need to change it from DbConnectionFactory to EntityConnectionFactory. Lastly, within the connection string itself, you need to change all instances of “& quot;” to an actual single quote. So ultimately that line should look like: _connection = Effort.EntityConnectionFactory.CreateTransient(“metadata = res://*/ … rest of conn string …”);

      1. However this applies only when you are using DatabaseFirst with db designer (.edmx)

  5. Hi,

    mocking out the db layer was getting too much so swapped to this approach however, I have code to handle DbUpdateConcurrencyException, as below

    try
    {
    await db.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
    if (!Exists(account.Id))
    {
    return false;
    }
    else
    {
    throw;
    }
    }

    How do I now test the case where the exception is raised. If I don’t then the coverage figures for my repos will be affected, I know its not the be all and end all of measures but quite high in everyones list of things to validate.

    Thanks

    Ian

    1. Worked it out! If I read a record, delete it then update it I get the if branch tested, Testing the else still seems a little difficult to test.

  6. Thank you for this Mateusz. I’m experiencing a similar issue to Johny Mays where it’s reading/writing to the actual database. Any assistance would be much appreciated.

    1. The default connection factory is set in this case in App.config.
      Maybe you have different connection factory set somewhere?

  7. Fantastic! It saddens me that this has been available for so long and I haven’t noticed until just now 🙂

    I ran into an issue with my current project though, related to having a constructor `public MyContext() : base(“name=Foo”) { }`; for some reason, it seems that Effort doesn’t manage to intercept the database provider. I’ve posted some more details, as well as a downloadable MWE, here: https://github.com/tamasflamich/effort/issues/83

    Do you have any suggestions on how to resolve that?

  8. Would really like to see a working download of this… Been strugging for 2 days to get it working from the snippets (which are incomplete).

      1. Thanks… Just pulled down [as ZIP] and opened with VS-2017. Attempted to build. Looks like your NuGet package management is not set up well (not to mention that the packages themselves have been checked in)…. Getting lots of build errors….

        1. more info… The directories exist under “..packages” but the DLL’s are not there. Since the directories exist, NuGet restore does not attempt to re-download.

          1. Looks good now…. Also, just as an FYI, I updated the package references to the latest update, and it works with them also 🙂

Comments are closed.