Author: Tom McClean
Updated: 02 Oct 2016 19:09
Effort is an Entity Framework Unit Testing tool. I needed to use it recently on a personal project so that I could Unit Test new and existing functionality, I hadn't done this until now for the reasons I describe later in this post.
This post goes through the difficulties with introducing Unit Tests retrospectively and how Effort can solve some common problems for developers trying to effectively Unit Test their code. This Post shows you how we set it up so that you can do the same.
The technology stack this guide was written alongside was C# MVC with Entity Framework 6 and StructureMap for Dependency Injection. If you get stuck and want to see a working example - you can check my code on GitHub - https://github.com/tommcclean/PortalCMS
A big problem you face when you use Entity Framework and want to Unit Test your code, is that if you use the EF Context in your tests directly like you do in code - you may make unexpected changes to your database.
Even if you can find a way of ensuring your test data does not affect actual users of your site, it's still not a good idea to allow it access to your database, here are a few reasons why...
You aren't supposed to be testing external systems like your database.
Your Unit Tests will not run very quickly if each test involves saving changes to your database.
Your tests could be contaminated by existing data in the database or data created by other tests.
You may accidentally write a test that affects live data and causes problems for users (or the stability) of your site.
So what are your options?
So while its clear that your Unit Tests should not be interacting with your website, there are many options you might look at for being able to write Unit Tests in other ways. They might include implementing a Repository Pattern in your code base or refactoring your methods to extract out database interactions.
Neither of those options are a good idea, why?
Firstly the Repository pattern doesn't cover Entity Framework's internal logic.
It is true that you shouldn't be writing Unit Tests for standard functionality provided by your libraries, but if you do completely remove Entity Framework from the equation as repository patterns tend to do, your services do not reflect the behaviour they will use in production. For example - How do you know a change you make won't cause Entity Framework to complain about keys or null values when saving an Entity?
What about the functionality you take for granted that doesn't work without Entity Framework, like the automatic population of primary keys, which affects the relationship between your entities.
If you aren't already using a Repository pattern in your code, it would also be very time consuming to change it all to utilise the new pattern you choose.
It is quite easy to find other architectural arguments against using a Repository pattern with Entity Framework online, here is an example: http://rob.conery.io/2014/03/04/repositories-and-unitofwork-are-not-a-good-idea/
Refactoring your code to extract out database interactions is not great either.
If you already have a large code base it would be a huge effort and a big risk to refactor it to extract out all database interactions leaving smaller testable methods and frankly your code would be worse for it.
This method also suffers a similiar problem to the Repository Pattern solution, in that by excluding Entity framework from the equation you aren't able to test that no exceptions are occuring in the committing of your changes after you make a change to your entities.
So what is a good idea?
I believe a package called "Effort" is the best solution. Effort creates a mock of your database in memory, this means Entity Framework performs exactly the same as it does in production systems but without actually touching your database. You will also get all of the benefits of Entity Framework like the linking of Entities and automatic population of keys.
I have only recently started using Effort, but so far it has proved incredibly good, although difficult to implement initially, this post would help you to reduce that pain though.
Essentially you add Effort via the NUGET Package Manager, setup a little bit of code to initialise it in your Unit Tests project and you are good to go. You can use Effort with Visual Studio's Test Solution in addition to others like NUnit. You can then use the context directly in your normal code and your Unit Tests cover everything that your production code uses.
One extra bonus to this package, is that if you haven't been thinking about Unit Testing until now, it won't involve a large scale refactoring of your existing code base to implement.
One caveat to note though is that you should remember not to test Entity Framework functionality specifically, but you can be sure that no exceptions are propagating out of your methods.
So how do you set it up?
Create a new project for your Unit Tests if you don't have one already. You can just add a "Unit Test Project" in your language of choice. The convention for mine was to use the name of the project I am testing and add ".Tests" at the end to denote that it was Unit Tests.
Install "Effort.EF6" from the Nuget Package Manager, make sure to get the right one, there is a package called "Effort" which is the same technology, but doesn't work with Entity Framework 6.
Create a Unit Test Class with an initialisation method like below. Make sure to add the right attributes to the class as seen in the screenshot below.
The Initialise() method is where Effort does its work, first of all you create an object scoped to the Class that reflects your Entity Framework Context, and then you assign it a reference within the Initialise method using the Effort Connection Factory.
When you do this Effort returns the EF Context for you to inject into your service layer, It is exactly the same object as it would be in production so your services don't need to change. The only difference is that all of your changes are saved in memory, not to your database.
The next part of the setup caused me several hours of confusion but was simple enough in the end, in order to use Effort; your Entity Framework DBContext needs to have a constructor allowing for a DbConnection, so you need to go away now and add that.
You don't want to remove the existing constructor, just add this one (with the DbConnection parameter) in addition to that.
This is important because by default Entity Framework creates the DBContext with no parameters, therefore if you use a Dependency Injection tool like StructureMap it will cease to work if you do not tell it how to register your DBContext. Effort uses the constructor with the DbConnection parameter, and your website uses the constructor with no parameters as it pulls your connection details from your configuration file.
To make this work you need to tell StructureMap which constructor to use, if you don't StructureMap will always try to use the constructor with the DbConnection parameter because it picks the greediest of all constructors until told otherwise.
You can tell StructureMap which constructor to use by doing the below...
If you have done all of that, you should now be able to write Unit Tests without fear of your database being contaminated, and your website should operate as normal!
For those new to Effort.EF6, if you setup your initialisation method the same way I have, it will give you a clean database (In memory of course) for every individual unit test
An example Unit Test I wrote is displayed below for information purposes.
Let me know in the comments if this guide helped, or if you ran into any problems.