Search code examples
c#unit-testingasp.net-coreasp.net-core-mvcmoq

Controller ViewResult returns empty Model collection while doing Unit test using xUnit and Mock


When i debugging the controller index method it returns an empty model where I do mistake? I am using Moq by Daniel Cazzulino and xUnit

Following is my controller code:

TeacherController:

public class TeacherController : Controller
{
    private IClassScheduleUnitOfWork _classScheduleUnitOfWork { get; set; }

    public TeacherController(IClassScheduleUnitOfWork classScheduleUnitOfWork)
    {
        _classScheduleUnitOfWork = classScheduleUnitOfWork;
    }

    public ViewResult Index()
    {
        var options = new QueryOptions<Teacher> {
            OrderBy = t => t.LastName
        };
        IEnumerable<Teacher> list = _classScheduleUnitOfWork.teachers.List(options);
        return View("Index", list);
    }
}

Here is my Unit test controller code:

public class TeacherControllerTests
{
    private readonly Mock<IClassScheduleUnitOfWork> _mock;

    public TeacherControllerTests()
    {
        _mock = new Mock<IClassScheduleUnitOfWork>();
    }

    [Fact]
    public void IndexActionMethod_ReturnsAViewResult()
    {
        var options = new QueryOptions<Teacher>
        {
            OrderBy = t => t.LastName
        };

        IEnumerable<Teacher> teachersList = GetAllTeachers();
 
        _mock.Setup(x =>x.teachers.List(options))
            .Returns(teachersList);

        var controller = new TeacherController(_mock.Object);

        //act
        var result = controller.Index();
        var actualData = (IEnumerable<Teacher>?)result?.ViewData.Model;
        Assert.Equal(5, actualData?.Count());

   }

   private IEnumerable<Teacher> GetAllTeachers()
   {
        IEnumerable<Teacher> teachers = new List<Teacher>()
        {
           new Teacher { TeacherId = 1, FirstName = "Anne", LastName = "Sullivan" },
           new Teacher { TeacherId = 2, FirstName = "Maria", LastName = "Montessori" },
           new Teacher { TeacherId = 3, FirstName = "Martin Luther", LastName = "King" },
           new Teacher { TeacherId = 4, FirstName = "", LastName = "Aristotle" },
           new Teacher { TeacherId = 5, FirstName = "Jaime", LastName = "Escalante" }
       };

       teachers = teachers.OrderBy(t => t.LastName).ToList();

       return teachers;
   }
}

Here is the definition of IClassScheduleUnitOfWork

 public interface IClassScheduleUnitOfWork
 {
     public IRepository<Teacher> teachers { get; set; }
     public IRepository<Class> classes { get; set; }
     public IRepository<Day> days { get; set; }

     public void Save();

 }

Here is the IRepository code:

 public interface IRepository<T> where T : class
 {
     IEnumerable<T> List(QueryOptions<T> options);
     T? Get(int id);
     T? Get(QueryOptions<T> queryOptions);
     void Insert(T entity);
     void Update(T entity);
     void Delete(T entity);
     void Save();
 }

Solution

  • The problem is that the QueryOptions<Teacher> object (options) in the test is not the same object as the QueryOptions<Teacher> object (options) in the System Under Test (SUT).

    Thus, the expression x =>x.teachers.List(options) will never be matched.

    You can loosen the Setup a bit to make the test pass:

    _mock.Setup(x => x.teachers.List(It.IsAny<QueryOptions<Teacher>>()))
        .Returns(teachersList);
    

    If it were me, though, I'd re-evaluate my options, since this is a test-unfriendly design for that very reason.