Search code examples
unit-testingasp.net-coremoqxunit

How do I test the Asp.Net Core controller which has Db Context and Logger?


I have a controller with two parameters and need to test them via unit tests. Want to test 4 parameters, ViewBug, etc. But how I can make fake DB context and logger? I'm stuck at this moment:

        [Fact]
    public void IndexReturnsAViewResultWithAListOfUsers()
    {
        // Arrange
        var mock = new Mock<AircraftsController>();
        var controller = new AircraftsController(/*params*/);

        // Act

        // Assert
    }

This is my controller:

 public class AircraftsController : Controller
{
    #region DbContext, Logger
    public AppDbContext Context { get; }
    private readonly ILogger<AircraftsController> _logger;

    public AircraftsController(AppDbContext context, ILogger<AircraftsController> logger)
    {
        Context = context;      
        _logger = logger;
        _logger.LogDebug(1, "NLog injected into Controller");
    }
    #endregion



    [HttpGet]
    public IActionResult Compare(int vehicle1, int vehicle2, int vehicle3, int vehicle4)
    {
        var planesFromDb = Context.Planes.OrderBy(x => x.BR).ToList();
        planesFromDb.Insert(0, new Plane { Image = "~/images/EmptyPlane.png", Nation = "EmptyFlag", Name = "Select aircraft", VehicleId=0 });
        var selectedPlanes = new List<Plane>();
        ViewBag.AllPlanesSelected = planesFromDb;

        selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle1));
        selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle2));
        selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle3));
        selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle4));

        _logger.LogInformation("Log Message");

        return View(selectedPlanes);
    }

}

Solution

  • As Stephen has suggested the in-memory provider is a good option for mocking an EFCore db context. It works for most things.

    I have a requirement to use Moq for 3rd party dependencies so I'd create mocks for both. For the db context I'd use EntityFrameworkCore.Testing (disclaimer, I am the author):

    var mockedDbContext = Create.MockedDbContextFor<AppDbContext>();
    

    Then for the logger I'd create a mock using Mock.Of

    var mockedLogger = Mock.Of<ILogger<AircraftsController>>();
    

    Easy one liners that you can then use to create your controller in your unit test. Overall I am an advocate of using the EFCore in-memory provider if it suits the unit test. Using mocks does have other advantages such as allowing you to verify invocations.