I'm trying out .NET Core for the first time and seeing how Moq can be used in unit testing. Out of the box, the controllers are created where the ApplicationDbContext are parameters to the constructor like this:
public class MoviesController : Controller
{
private readonly ApplicationDbContext _context;
public MoviesController(ApplicationDbContext context)
{
_context = context;
}
Here is the unit test that I started 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 ApplicationDbContext is a concrete class AND it does not have a parameterless constructor so the test won't work. It gives me error: Could not find parameterless constructor.
Perhaps this may be a question more aimed at Moq rather than it being related to .NET Core, but I'm also new to Moq so I'm not sure how to proceed. Here is how the ApplicationDbContext code was generated when I created the project:
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 so that my unit test would succeed?
UPDATE:
I discovered from https://msdn.microsoft.com/en-us/magazine/mt703433.aspx that you can configure EF Core to use an in-memory database for unit testing. So I changed my unit test to look like this:
[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 now succeeds. But is this the proper way of doing this? Obviously, I completely eliminated mocking the ApplicationDbContext with Moq! Or is there another solution to this problem using Moq.
ApplicationDbContextimplements ` IdentityDbContext<ApplicationUser>`, could you possibly inject the interface into your controller and then mock that instead? - Corporalis