我是单元测试的新手。我有一个带有依赖注入的控制器 - 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;
}
}
有人可以解释一下,如何纠正我的代码?
答案 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);