crackedcornjimmy
crackedcornjimmy

Reputation: 1992

How to Unit Test an MVC Controller with Three Dependencies, Using Moq

Caveat: I'm brand spanking new to Moq, Unit Testing with Moq, and TDD, generally.

I have a controller with three dependencies. Here is the Constructor (etc) to the SomethingController:

public class SomethingController : Controller
    {
        private readonly ILogger<SomethingController> _logger;
        private readonly ISomethingRepository _something;
        private readonly IExceptionSvc _ex;

        public SomethingController(ILogger<SomethingController> logger,
            ISomethingRepository something,
            IExceptionSvc ex)
        {            
            _logger = logger;
            _something = something;
            _ex = ex;

        }

[HttpGet]
        [AllowAnonymous]
        public JsonResult GetStuff()
        {
            //intitialize list
            var stuffs = new List<StuffViewModel>();

        try
        {
            _logger.LogInformation("SomethingController: GetStuff() - Getting Stuff, sorted ascending.");

            //get the stuffs
            stuffs = _something.GetStuff();

            _logger.LogInformation("Retrieved {0} Stuffs.", stuffs.Count);
        }
        catch (Exception ex)
        {
            _logger.LogError("Error in SomethingController: GetStuff()", ex);
            return _ex.Http500ErrorReturn("GetStuff");
        }

        return Json(stuffs);
    }

}

Now, here is the description of what each dependency is for:

ILogger: This is simply for logging, using Microsoft.Extensions.Logging via NLog. ISomethingRepository: This does all the work (well...the impementation of it, really). It calls the database and gets things. There is a method called GetStuff(), which simply returns a list of Stuffs. This Repository has a dependency on the database, which is injected into the repo via the constructor.
IExceptionSvc: This is just a dumb little service that has a method that returns a 500 Error response for a JSON formatted return to the caller.

I want to test the GetStuff() action in the Controller, which will call the GetStuff() implementation in the Repository.

I currently have the following code stubbed in, in my test project:

public void GetStuff_Is_Awesome()
        {
            Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
            Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
            Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
            SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);

sc.GetStuff();

//...what now? What am I looking for? Am I going to see a list of stuffs here?

}

I just want to know what to expect? Am I testing whether a list of Stuffs was returned? How do I test that?

Here is the implementation of GetStuff() in the Repository (with the repo constructor for reference sake):

private readonly ApplicationDbContext _context;
        private readonly ILogger<SomethingRepository> _logger;

        public SomethingRepository(ApplicationDbContext context,
            ILogger<SomethingRepository> logger)
        {
            _context = context;
            _logger = logger;
        }

public List<StuffViewModel> GetStuff()
        {
            List<StuffViewModel> stuffs = null;
            stuffs = _context.Stuffs.OrderBy(b => b.Name).Select(b => new StuffViewModel
                {
                    Id = b.Id,
                    Name = b.Name
                }).ToList();

return stuffs;
            }

Upvotes: 1

Views: 1100

Answers (1)

Alex
Alex

Reputation: 38509

Think of it as Arrange, Act Assert

public void GetStuff_Is_Awesome()
{
    //arrange
    Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
    Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
    Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
    SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);

    //act
    sc.GetStuff();

    //assert
}

So, you want to assert that something happened, or something was called.

Without knowing more about your domain, or the ISomethingRepository, you could Assert that the method was called:

something.Verify(m => m.MethodToCheckIfCalled());

Other options would be to setup the return values for MethodToCheckIfCalled and assert the results etc...

Assuming a GetStuff method on your controller that looks like:

public ActionResult GetStuff()
{
    var data = _something.GetFromRepo();
    return View(data);
}

I would do something like:

public void GetStuff_Is_Awesome() //please don't call it this
{
    //arrange
    Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
    Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
    Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
    SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);

    //setup your something mock GetFromRepo method to return a List of StuffViewModel
    something.Setup(m=> m.GetFromRepo())
        .Returns(new List<StuffViewModel>(){Id = 1, Name = "Test"});

    //act
    var result = sc.GetStuff();

    //assert

    //safely cast result to ViewResult
    var viewResult = result as ViewResult;

    Assert.IsNotNull(viewResult);
    Assert.IsNotNull(viewResult.Model); 

    // add additional checks on the Model.. loads of ways of doing this
    var viewResultModel = viewResut.Model as List<StuffViewModel>;

    Assert.AreEqual(1, viewResultModel.First().Id);
    Assert.AreEqual("Test", viewResultModel.First().Name);
}

Upvotes: 4

Related Questions