Recently, the data access technology that we have relied on for most of our projects is Entity Framework. Entity Framework is an excellent tool that makes it easy to work with a database using the .NET code that we are most familiar with. However, out of the box, Entity Framework does little to help support automated unit testing. Specifically, the DbContext is not interface-based and does not provide a clean way to support mocks. This makes it difficult to write a unit test that doesn’t require database access, which would result in slow and error-prone tests.

Fortunately, there are ways to extend these objects and attach interfaces to them so that they can be mocked and support unit testing of the data layer without direct access to a database.

Dependency Injection

As I mentioned in my previous post, dependency injection is a technique that allows us to inject mock objects into a target class so that we can test the target class without actually hitting code that we don’t intend to such as data access code.

To start with, we’ll begin with an implementation of a CurrencyData class that is responsible for retrieving Exchange Rates from a database.

Effective Unit Testing 1

The first thing we can see is that the class is instantiating a DatabaseEntities class which is an Entity Framework DbContext. Unfortunately, this means that if we attempt to test this class, the unit test will attempt to access the database and will fail.

Using dependency injection techniques, we’ll go ahead and extract an interface for DatabaseEntities and use that instead so that we can support mocking the DatabaseEntities object. To do this, we’ll start with an empty interface named IDatabaseEntities that we will add to later. We will also add the following partial class that attaches the interface to our DatabaseEntities object:

Effective Unit Testing 2

Now, we can refactor the CurrencyData class to use the interface and provide a constructor where we can pass in the mocked DatabaseEntities. After this refactoring, our CurrencyData class should look like the following:

 Effective Unit Testing 3

At this point, you should have a build failure since our interface does not actually contain the ExchangeRates property that the CurrencyData class is using. We’ll add the ExchangeRates property to our interface as follows:

Effective Unit Testing 4

Note that I declared the property as an IQueryable<ExchangeRate> rather than a DbSet. DbSet implements the IQueryable interface, and whenever I access this data I really only need the capabilities provided by the IQueryable interface. This makes it easier to write tests and set up the data that I am expecting.

Then, we’ll update the partial class to ensure that it implements this property:

Effective Unit Testing 5

Now, our solution should compile and we are ready to write a unit test for this method.

Writing a Test for a Database Query

For my first test, I will want to read some data from the database. To set up this test, I will create a mock of the IDatabaseEntities. In this example, I will use Moq as the mocking framework so that I don’t have to write my own mock.

To begin with, I will create an initialization method that will set up my target for testing:

Effective Unit Testing 6

To write this test, I will set up the data I intend to use by creating a list and then calling the extension method AsQueryable to convert it into an IQueryable. When finished, the test will look like the following:

Effective Unit Testing 7

Now, we have written a unit test where we control the data without needing to set up a database and we can test the data access method. In this example, for clarity, I showed how to write the class first so that it would be testable. Once this pattern becomes familiar, a more test-driven approach can be taken and it can become second nature to write the tests first, knowing that your data access layer will perform as you expect it to.

For my next post, I’m planning on showing how this same technique can be used to unit test and validate data access code that inserts, updates, or deletes records from a database.