How to make Moq ApplicationDbContext in .NET Core

This is the first time I'm testing .NET Core and see how Moq can be used in unit testing. Controllers are created out of the box, where ApplicationDbContext are constructor parameters, for example:

public class MoviesController : Controller { private readonly ApplicationDbContext _context; public MoviesController(ApplicationDbContext context) { _context = context; } 

Here is the unit test that I started working with when testing the controller:

 [TestClass] public class MvcMoviesControllerTests { [TestMethod] public async Task MoviesControllerIndex() { var mockContext = new Mock<ApplicationDbContext>(); var controller = new MoviesController(mockContext.Object); // Act var result = await controller.Index(); // Assert Assert.IsInstanceOfType(result, typeof(ViewResult)); } 

But then I realized that ApplicationDbContext is a concrete class. And it does not have a constructor without parameters, so the test will not work. This gives me an error: I could not find a constructor without parameters.

It may be a question that is more Moq-focused rather than .NET Core related, but I'm also new to Moq, so I'm not sure how to do this. Here's how the ApplicationDbContext code was generated when the project was created:

 public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Customize the ASP.NET Identity model and override the defaults if needed. // For example, you can rename the ASP.NET Identity table names and more. // Add your customizations after calling base.OnModelCreating(builder); } public DbSet<Movie> Movie { get; set; } } 

What do I need to change for my unit test to succeed?

UPDATE:

I found from https://msdn.microsoft.com/en-us/magazine/mt703433.aspx that you can configure EF Core to use the in-memory database for unit testing. So I changed my unit test as follows:

  [TestMethod] public async Task MoviesControllerIndex() { var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>(); optionsBuilder.UseInMemoryDatabase(); var _dbContext = new ApplicationDbContext(optionsBuilder.Options); var controller = new MoviesController(_dbContext); // Act var result = await controller.Index(); // Assert Assert.IsInstanceOfType(result, typeof(ViewResult)); } 

This test succeeds now. But is this the right way to do this? Obviously, I completely eliminated the mockery of ApplicationDbContext with Moq! Or is there another solution to this problem using Moq.

+6
source share
3 answers

You should not try to mock the database context directly. Instead, create a repository template and instead create a repository.

Here is a great guide on how to properly implement the template.

+4
source

Failure of the DbContext does not work, because there are too many providers that must make it work. A simpler solution is to use the InMemory solution that Microsoft has implemented for this specific purpose. Please do not create repos just for testing (which still does not check EF code).

Here is a link on how to test with InMemory databases in the .net core

https://docs.efproject.net/en/latest/miscellaneous/testing.html

+4
source

If anyone has interest, I got some of @Corporalis idea and implemented an interface to demonstrate ApplicationDbContext in a test project.

 public interface IApplicationDbContext { DbSet<Movie> Movies { get; set; } int SaveChanges(); } 

And in ApplicationDbContext

  public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext { public virtual DbSet<Movie> Movies { get; set; } public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); } } 

Remember to register the interface in StartUp.cs, ConfigureServices

 services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>()); 

So now in the controller or service or each method that you should use ApplicationDbContext, make this a reference to the interface instead

 public class filterMovies { private readonly IApplicationDbContext _context public filterMovies(IApplicationDbContext context) { _context = context; } } 

Now we will boldly laugh at ApplicationDbContext using the interface instead of implementing it self

  var mockContext = new Mock<IApplicationDbContext>(); mockContext.Setup(mc => mc.Movies).Returns(mockDbSet.Object); 

I hope this helps;)

+1
source

All Articles