When working with Entity Framework in a test-driven manner, we need to be able to slip a layer between our last line of code and the framework. This allows us to simulate the database connection without actually hitting the database.
We will be using NuGet Package Manager to install the Entity Framework 4.1 assemblies.
The package installer can be found at http://www.nuget.org/.
We will also be using a database for connecting to the data, and updating it.
Open the Mocking the Database solution in the included source code examples.
Execute the database setup script from the code samples included with this recipe. This can be found in the DataAccess
project within the Database
folder.
BusinessLogic
project, add a new C# interface named IDbContext
using the following code:using System.Linq; namespace BusinessLogic { public interface IDbContext { IQueryable<T> Find<T>() where T : class; } }
Test
project to test so we can supply false results from a fake database with the following code:using System.Collections.Generic; using System.Linq; using BusinessLogic; using DataAccess; using Microsoft.VisualStudio.TestTools.UnitTesting; using Rhino.Mocks; namespace Test { [TestClass] public class QueryTest { [TestMethod] public void ShouldFilterDataProperly() { //Arrange IDbContext mockContext = MockRepository.GenerateMock<IDbContext>(); mockContext.Expect(x => x.Find<Blog>()).Return(new List<Blog>() { new Blog(){Id = 1,Title = "Title"}, new Blog(){Id=2,Title = "no"} }.AsQueryable()); //Act var items = mockContext.Find<Blog>().ToList(); //Assert Assert.AreEqual(2,items.Count()); Assert.AreEqual("Title",items[0].Title); Assert.AreEqual("no",items[1].Title); } } }
DataAccess
project, create a new C# class named BlogContext
with the following code:using System.Data.Entity; using BusinessLogic; namespace DataAccess { public class BlogContext : DbContext, IDbContext { public BlogContext(string connectionString) : base(connectionString) { } public DbSet<Blog> Blogs { get; set; } public IQueryable<T> Find<T>() where T : class { return this.Set<T>(); } } }
BusinessLogic
project, add a new C# interface called IDbContext
with the following code:using System.Linq; namespace BusinessLogic { public interface IDbContext { IQueryable<T> Find<T>() where T : class; } }
The mocking framework that we are using (called RhinoMocks) allows us to pass a fake object which can simulate the responses that a database would provide for us without having that connection. This allows us to keep our tests from being dependent on SQL data, and therefore brittle. Now that we have data available from our mock, we can test whether it acts exactly like we coded it to. Knowing the inputs of the data access code, we can test the outputs for validity.
This layering is accomplished by putting our Find
method as an abstraction between the public framework method of Set<T>
and our code, so we can change the type to something constructible. This is required due to the constructors of DbSet<T>
being internal (not callable from any other assembly). By layering this method, we can now control every return from the database in the test scenarios.
This layering also provides for the better separation of concerns, as the DbSet<T>
in Entity Framework mingles multiple independent concerns, such as connection management and querying, into a single object. We will continue to separate these
concerns in future recipes.
Testing to the edges of an application requires that we adhere to certain practices which allow us to shrink the untestable sections of the code. This will allow us to unit test more code, and make our integration tests far more specific.
An important point to remember while performing unit testing is that we should only be testing a single class. The point of a unit test is to ensure that a single unit, a single class, performs the way we expect it to.
This is why simulating classes that are not under test is so important. We do not want the behavior of these supporting classes to affect the outcomes of unit tests for our class under test.
Often, it is equally important to test the actual combination of your various classes, to ensure they work properly together. These integration tests are valuable, but are almost always more brittle, require more setup, and are run slower than the unit tests. We certainly need integration tests on any project of a reasonable size, but we want unit tests first.
Most unit tests can be viewed as having three parts: arrange, act, and assert. Arrange
is where we prepare the environment to perform the test, for instance, mocking of the IDBContext
and setting up an expectation that Find<T>
will be called. Act
is where we perform the action under test and is most often a singular line of code. Assert
is where we ensure that the proper result was reached. Note the comments in the examples above that call out these sections. We will use them throughout the book to make it clear what the test is trying to do.