我的一个控制器中有以下方法: -
//GET: api/Books/5
[ResponseType(typeof(BookDetailDTO))]
public async Task<IHttpActionResult> GetBook(int id)
{
var includeProperties = "Author,Genre";
var item = _bookRepository.GetBookById(id, includeProperties);
//var item = await Task.Run(() => _bookRepository.GetBookById(id, includeProperties));
}
我试图针对它创建一个单元测试: -
[Test]
public async Task GetBook_Should_Get_A_Single_Book_By_Its_ID()
{
// Arrange
_booksRepositoryMock.Setup(x => x.GetBooks("Author")).Returns(books.AsQueryable());
// Act
var result = await objController.GetBook(1) as OkNegotiatedContentResult<Book>;
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(books[1].Title, result.Content.Title);
}
问题在于以下一行: -
var item = _bookRepository.GetBookById(id, includeProperties);
item
始终为空,因为现在模拟的_bookRepository
未被命中。但是,在运行实际应用程序时,它工作正常,我通过ID
得到了该书。
如何调整测试/代码并使其成功运行?
感谢您的帮助和时间
答案 0 :(得分:0)
我看待它的方式你没有测试正确的东西。
您正在测试的是您的数据库层,因此您可以取消所有异步内容。
单元测试是关于一个非常精细的级别并测试一个简单的事情,而不是一个命中数据库的api异步调用。这是一个集成测试,而不是单元测试。
因此,请针对您的数据库层编写测试方法,并将其保留。如果db层正常运行,你的api将返回正确的东西。
更新
这是我要做的。
为您的数据库层编写单元测试。保留当前的模拟代码,设置模拟书对象并检查在触发GetBookByID方法时是否获得它。那是在图书回购级别,所以你正在测试你是否正确。它可能是发生此测试的BookRepoTest类。
现在,我会在你的repo和你的控制器之间添加一个额外的层。获取数据的一般方法,想象一个看起来像这样的层:
public DataAccess class
{
IRepo _repo;
public DataAccess( IRepo repo){
_repo = repo;
}
public GetRepo(){
return _repo;
}
}
当然会有更多的代码,但想法是注入依赖。现在你可以模拟这种依赖,而不用触及你的实际回购。然后你继续检查你的控制器,当输入模型错误时,当id不存在等时会发生什么。
现在您已将代码分开并使其更易于测试。在构建项目时,我总是这样做。当你在你的控制器中看到像UnitOfWork这样的东西的时候,已经有一种气味,你没有足够的关注点分离。 根据您当前的代码,一旦调用UnitOfWork方法,实际上是否使用了这个模拟的repo,它甚至都不清楚。
您甚至可以将repo依赖项(基于接口)添加到控制器本身的构造函数中。然后你也可以注入这种方式。无论哪种方法更容易适用于您。