Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing Entity Framework 6 with IdentityDbContext

I am currently in the process of adding unit tests for the Entity Framework 6 operations in my ASP.NET Web API 2.0 application, using the following MSDN tutorial:

http://msdn.microsoft.com/en-us/data/dn314431

The tutorial suggests creating an interface to use in place of the application's DbContext class. This interface is then used throughout the application, allowing the controllers to be tested using a 'mocked' test context.

Below is the suggested interface from the tutorial:

public interface IBloggingContext 
{ 
    DbSet<Blog> Blogs { get; } 
    DbSet<Post> Posts { get; } 
    int SaveChanges(); 
}

As I am using SaveChangesAsync() instead, I have modified the interface to the one below:

public interface IApplicationDbContext : IDisposable
{
    DbSet<Blog> Blogs { get; } 
    DbSet<Post> Posts { get; } 
    Task<int> SaveChangesAsync();
}

Unfortunately this tutorial assumes that the context has a base class of DbContext, when mine uses IdentityDbContext as its base class (see below). There seems to be no way of calling SaveChangesAsync() - which I am using in place of SaveChanges() - from my context, leaving it unable to implement the interface correctly.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {

    }

    public Task<int> SaveChangesAsync()
    {
        // No way to call DbContext.SaveChangesAsync() from here
    }
}

Any ideas or suggestions would be much appreciated!

EDIT:

Below are copies of the classes which are currently in use:

ApplicationDbContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {

    }

    public Task<int> SaveChangesAsync()
    {
        // Cannot resolve base.SaveChangesAsync() from here
    }

    public DbSet<Product> Products { get; set; }
}

IApplicationDbContext

public interface IApplicationDbContext
{
    Task<int> SaveChangesAsync();
    DbSet<Product> Products { get; set; }
}

Example Controller Constructor (which uses the interface)

protected BaseEntityController(IApplicationDbContext context)
{
    db = context;
}

EDIT 2:

It does appear that there could be an issue with the class hierarchy. Below is another (hopefully related) build error:

UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));

Error   2   Argument 1: cannot convert from 'IApplicationDbContext' to 'System.Data.Entity.DbContext'

For reference, my ApplicationUser class has a base class of IdentityUser:

public class ApplicationUser : IdentityUser

EDIT 3:

As mentioned by Jon, the error posted in EDIT 2 is to be expected. Unfortunately, the error also occurred for the context itself (ApplicationDbContext) rather than solely for the interface (IApplicationDbContext).

The issue has now been resolved, but only by uninstalling and reinstalling the packages from the solution, restarting Visual Studio, and then rebuilding the project. I am still unsure of the exact cause of the issue.

I have marked Jon's answer as correct, as it would be under normal circumstances.

like image 920
Andrew Sula Avatar asked Jan 15 '15 13:01

Andrew Sula


1 Answers

According to MSDN here IdentityDbContext inherits from DbContext., so if you need to call directly then base.SaveChangesAsync should work.

However, really you don't need to implement that method, since its already implemented by DbContext in the "real" context unless you're using an explicit interface implementation or adding more code (which you're not in the example). You do need to implement something in the TestDbContext since it won't be inheriting from DbContext.

If I implement SaveChangesAsync as you have like this:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext
{
    public Task<int> SaveChangesAsync()
    {
        return base.SaveChangesAsync();

        // No way to call DbContext.SaveChangesAsync() from here
    }
}

Then on build I get this warning:

Warning 1 'SOWorking.ApplicationDbContext.SaveChangesAsync()' hides inherited member 'System.Data.Entity.DbContext.SaveChangesAsync()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

The build has no warnings if I either omit the implementation of SaveChangesAsync entirely, or add override like this:

public override Task<int> SaveChangesAsync()
{
    return base.SaveChangesAsync();
}

However, unless you have anything else to do in SaveChangesAsync then this is pointless (and Resharper greys it out for you as such).

like image 190
Jon Egerton Avatar answered Sep 24 '22 12:09

Jon Egerton