Thursday 20 November 2014

MOCKING ENTITY FRAMEWORK 6 CONTEXT WITH RHINO MOCKS (CODE FIRST)


Standard

NOTE: You must be using Rhino.Mocks 3.6.1 or higher.
As we have decided to use Entity Framework 6 for all our projects going forward we recently had some issues mocking out the context class for unit testing the next layer up from the context. Most of my findings searching online gave me examples of how to mock the context using MOQ another mocking framework for unit testing. Ill leave the discussion of the different mocking frameworks for another post or another blogger (there is lots of information out there).
A note about the setup… We use a code first entity framework setup which is how I am going to code this example.

First we have a business object (model) that is a POCO:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthdate { get; set; }
}
Second we must create the context:
public class ExampleContext : DbContext
{
       publicExampleContext()
            : base(“ExampleConnectionStringName”)
        { }
      public virtual IDbSet<Customer> Customers { get; set; }
}
Notice that the property for Customers in the context is virtual and is an IDbSet not a DbSet. This is important for being able to stub the value of the Customers property.
We now can create a method in a class that uses the context to get some data from the database that we will unit test. We will keep this simple:
public class CustomerRepository
{
private ExampleContext Context;

public CustomerRepository(ExampleContext context)
{
Context = context;
}

public IEnumerable<Customer> GetCustomersByBirthdate(DateTime birthdate)
{
return context.Customers.Where(x => x.Birthdate == birthdate).ToList();
}
}

We will now write our unit test to test the GetCustomersByBirthdate method. Notice the class constructor takes the context as an argument instead of creating it in the constructor of the class with the new keyword. This is to allow us to pass the mock context into the class to use, it promotes loose coupling of the classes. More can be learned about this by looking up Inversion of Control or Dependency Injection. In the unit test we need to setup a mock of the context and setup the fake data to be returned from the context when asked. Here is the Setup method for the unit tests:
public class CustomerRepositoryTests
{
protectedExampleContext MockContext;
protected IQueryable<Customers> MockData;
protected IDbSet<Customers> MockSet;
        [SetUp]
public void SetUp()
        {
            MockContext = MockRepository.GenerateMock<ExampleContext>();
            MockSet = MockRepository.GenerateMock<IDbSet<Customer>IQueryable>();
            MockData = new List<Customer>
            {
new Customer { Id= 119, FirstName = “Bob”, LastName = “Johnson”, Birthdate = new DateTime(1981, 1, 1) },
new Customer { Id = 120, FirstName = “Bob”, LastName = “Johnson”, Birthdate = new DateTime(1981, 1, 1) },
new Customer { Id = 118, FirstName = “Bob”, LastName = “Johnson”, Birthdate = new DateTime(1971, 10, 10) }
            }.AsQueryable();
            // These were hard to find
            MockSet.Stub(m => m.Provider).Return(MockData.Provider);
            MockSet.Stub(m => m.Expression).Return(MockData.Expression);
            MockSet.Stub(m => m.ElementType).Return(MockData.ElementType);
            MockSet.Stub(m => m.GetEnumerator()).Return(MockData.GetEnumerator());
            MockContext.Stub(x => x.Customers).PropertyBehavior();
            MockContext.Customers = MockSet;
        }
Notice that the hard to find part is stubbing items on the IQueryable interface and notice that when we create the mock for the IDbSet it is actually a multi-mock where more than one interface becomes part of the mock object. And here is the actual test using the mock context and calling the method.
        [Test]
      public void GetFactsByLocationId_SingleResult_ReturnsCorrectResult()
        {
         const int testId = 120;
         var testRepository = new CustomerRepository(MockContext);
            IEnumerable<Customer> result =testRepository.GetCustomersByBirthdate(testId);
            Assert.That(result.Count, Is.EqualTo(1));
        }
That is how you mock out the context. The part I struggled with was the setup method and making sure the DbSet properties are virtual and IDbSet instead. I hope this was helpful as I spend a bit of time trying to figure this out.

No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...