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
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 [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.
It would not work with pure SQL commands, things like ExecuteStoreCommand, ADO.NET 😉 Anyway when using ORM you should also forget about these.
For testing purposes lets imagine a Shop application that has:
SimpleShop.DataLayer (DbContext and Entity models)
SimpleShop.ServiceLayer (Logic and business models)
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:
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):
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.
Let us write some examples. First, some data preparation:
Something simple at the beginning, check if products are in proper categories (testing if Service using argument to separate categories):
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):
.NET Developer, working with Microsoft technologies for quite a long time. Expert on the best programming practices, always eager to encourage colleagues to write top quality code. His everyday duties revolve around software architecture, object-oriented programming and project patterns. In his free time he pursues his favourite hobby – cycling.
We process cookies and make them available to Google Analytics (a service provided by Google, Inc.) to improve the performance of the website, to learn your preferences about using it and to tailor it to your needs. The data will be anonymised before being transmitted. If you do not agree to this, you may disable cookies in your browser. If you do not change your browser settings, you accept the fact that it saves cookies.