1: public class Customer
2: {
3: public int Id { get; set; }
4: public string Name { get; set; }
5: }
6:
7: public class CustomerContext : DbContext, ICustomerContext
8: {
9: public DbSet<Customer> Customers { get; set; }
11: }
I now want to write some tests and I'm going to mock the DbContext using NSubstitute. I first try something like this:
1: [Test]
2: public void can_mock_customer_context()
3: {
4: var context = Substitute.For<CustomerContext>();
5: context.Customers.Returns(
6: new DbSet<Customer>(
7: new[]
8: {
9: new Customer {Name = "Sean"}
10: })
11: );
12: Assert.AreEqual(1, context.Customers.Count());
13: }
The problem is that the DbSet constructor is internal (as of EF4 Code First CTP5). So, let's abstract our DB access to a simple interface and replace DbSet<T> with the IQueryable<T> interface, ending up with the below:
1: public interface ICustomerContext
2: {
3: IQueryable<Customer> Customers { get; set; }
4: }
This interface can be implemented like so:
1: public class CustomerContext : DbContext, ICustomerContext
2: {
3: public DbSet<Customer> Customers { get; set; }
4: IQueryable<Customer> ICustomerContext.Customers { get { return Customers; } }
5: }
Now all we need to do is to use an implementation of IQueryable<T> in our mock. I'm going to use EnumerableQuery<T> which gives me the following test that now passes:
1: [Test]
2: public void can_mock_customer_context()
3: {
4: var context = Substitute.For<ICustomerContext>();
5: context.Customers.Returns(
6: new EnumerableQuery<Customer>(
7: new[]
8: {
9: new Customer {Name = "Sean"}
10: })
11: );
12: Assert.AreEqual(1, context.Customers.Count());
13: }
14: }
I'm new to NSubstitute but it seem to be the lowest friction mocking library out there. Just perfect for use with Entity Framework 4 Code First - certainly the lowest friction ORM there is today!
Note that we could have used the repository pattern to wrap the DbContext instead of a simple interface, the approach is almost identical.
Update: Ro Miller has an alternative approach using fakes that does a better job of surfacing IDbSet
5 comments:
public class CustomerContext : DbContext
{
public DbSet Customers { get; set; }
}
can be replaced by
public class CustomerContext : DbContext
{
public IDbSet Customers { get; set; }
}
negating the need for the additional property and allowing you to mock to your hearts content!
@Roja - thanks for the comment. I was unaware of the IDbSet interface. However, after replacing DbSet with this interface, I cannot see a way to get NSubstitute to return the values I need. The following test raises an exception. Maybe I'm missing something here?
public interface ICustomerContext
{
IDbSet Customers { get; set; }
}
public class CustomerContext : DbContext, ICustomerContext
{
public IDbSet Customers { get; set; }
}
[Test]
public void can_mock_customer_context()
{
var customers = Substitute.For>();
var customer = new Customer();
customers.Add(customer);
var context = Substitute.For();
context.Customers.Returns(customers);
Assert.AreEqual(1, context.Customers.Count());
}
IQueryable is not a suitable substitute for DbSet b/c it doesn't include the Add method.
I've updated the article with a link to another approach.
Alternatively, you can download my project on Codeplex. I have a working example that you can copy.
https://entityinterfacegenerator.codeplex.com/
It generates the interface files that you need for IoC purposes.
Post a Comment