使用ASP.net核心MVC应用程序中的List为View()创建单元测试

时间:2017-07-24 09:27:04

标签: c# entity-framework unit-testing asp.net-core asp.net-core-mvc

我是单元测试的新手。我有一个带有依赖注入的控制器 - StudentsController和我的Index()方法:

public class StudentsController : Controller
{
    public readonly UniversityContext _context;//Database
    public StudentsController(UniversityContext context)
    {
        _context = context;    
    }//Constructor with database
    // GET: Students
    public async Task<IActionResult> Index()
    {
        return View(await _context.Students.ToListAsync());
    }
}

接下来我需要编写一个正确的单元测试代码,检查是否:

  

1)View()有一个列表与我的学生

     

2)与学生的查询不为空。

我读过Mock对象,但我不知道如何编写正确的代码。我到目前为止编写的代码:

public class StudentsControllerTests
{
    [Fact]
    public async Task Index_ReturnsAViewResult_WithAListOfStudents()
    {
        var mockRepo = new Mock<UniversityContext>();
        mockRepo.Setup(repo => repo.Students.ToList()).Returns(GetTestStudents());//There i get following error:Expression references a method that does not belong to the mocked object
        var controller = new StudentsController(mockRepo.Object);

        // Act
        var result = controller.Index();

        //// Assert
        var viewResult = Assert.IsType<ViewResult>(result);
        var model = Assert.IsAssignableFrom<IEnumerable<Student>>(
            viewResult.ViewData.Model);
        Assert.NotNull(model);//Second Condition

    }
    public List<Student> GetTestStudents()
    {
        var sessions = new List<Student>();
        sessions.Add(new Student()
        {
            bDate = new DateTime(1994, 7, 2),
            Name = "Test One"
        });
        sessions.Add(new Student()
        {
            bDate = new DateTime(1995, 7, 1),
            Name = "Test Two"
        });
        return sessions;
    }
}

有人可以解释一下,如何纠正我的代码?

1 个答案:

答案 0 :(得分:2)

您只需要模拟上下文的成员,在本例中是.Students属性。 ToList是一个在属性上调用的扩展方法,不能被moq模拟。

同样.Students是一个DbSet,也需要进行模拟。

使用此答案中的测试类:

How to mock an async repository with Entity Framework Core

推导出以下通用扩展方法

public static class MockDbSetExtensions {
    public static Mock<DbSet<T>> AsDbSetMock<T>(this IEnumerable<T> list) where T : class {
        IQueryable<T> queryableList = list.AsQueryable();
        Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
        dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
        return dbSetMock;
    }

    public static Mock<DbSet<T>> ToAsyncDbSetMock<T>(this IEnumerable<T> source)
        where T : class {        
        var data = source.AsQueryable();        
        var mockSet = new Mock<DbSet<T>>();        
        mockSet.As<IAsyncEnumerable<T>>()
            .Setup(m => m.GetEnumerator())
            .Returns(new TestAsyncEnumerator<T>(data.GetEnumerator()));        
        mockSet.As<IQueryable<T>>()
            .Setup(m => m.Provider)
            .Returns(new TestAsyncQueryProvider<T>(data.Provider));        
        mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => data.GetEnumerator());        
        return mockSet;
    }

}

使用上述实用程序,更新

 mockRepo.Setup(repo => repo.Students.ToList()).Returns(GetTestStudents());

var studentsMockedDbSet = GetTestStudents().ToAsyncDbSetMock();
mockRepo.Setup(repo => repo.Students).Returns(studentsMockedDbSet.Object);