我正在尝试测试我制作的WebApi控制器。我尝试使用依赖注入来简化测试。尽管它实际上具有相反的效果。
我目前有一个控制器,它在其构造函数中采用 repo 接口。存储库接口在其构造函数中也采用 DbContext 接口。我是否正确认为我需要模拟 DbContext,并在模拟存储库时将该模拟上下文作为参数传递,然后将该模拟存储库传递到我正在测试的控制器的实际实现中?
我正在使用最小起订量和 NUnit。
谢谢
我假设你在谈论单元测试,因为你正在使用模拟。
你不需要模拟比你正在单元测试的类所依赖的第一级接口更深的模拟。 在您的示例中,您的controller
取决于接口,让我们调用 IRepository
。 您对IRepository
的实现反过来又需要IDbContext
。 注意上一句中的粗体/斜体,只要你嘲笑的是IRepository
的接口,那么IDbContext
就没有任何关系——IDbContext
是你的具体仓库的依赖,但不是你的IRepository
。
您的IRepository
应该拥有控制器模拟与控制器单元测试相关的数据/行为所需的一切。
例:
public class MyController : MyController
{
private readonly IRepository _repo;
public MyController(IRepository repo)
{
_repo = repo;
}
public IActionResult GetData(string userId)
{
var data = _repo.GetUserInformation(userId);
var someTransformation = null; // transform data
return View(someTransformation);
}
}
public interface IRepository
{
MyObject GetData(string userId);
}
public class Repository : IRepository
{
private readonly IDbContext _iDbContext;
public Repository(IDbContext iDbContext)
{
_iDbContext = iDbContext;
}
public MyObject GetUserInformation(string userId)
{
return _iDbContext.MyObjects.Where(w => w.UserId == userId);
}
}
public interface IDbContext
{
// impl
}
public class DbContext : IDbContext
{
// impl
}
为了测试MyController
,您依赖于接口IRepository
。 反过来,您的IRepository
(Repository
)的实现依赖于什么并不重要,因为这与测试MyController
的范围无关。
在您的测试中:
public class MyControllerTests
{
public void MyTest()
{
Mock<IRepository> mockRepo = new Mock<IRepository>();
mockRepo
.Setup(s => s.GetUserInformation(It.IsAny<string>())
.Returns(new MyObject()
{
UserId = "whatever", // or whatever the mocked data needs to be
DateCreated = DateTime.MinValue
});
}
}
在上面的测试中,我们提供了调用GetUserInformation
时IRepository
应该返回的示例数据,我们不依赖于实际的DbContext
,甚至不依赖于IDbContext
,因为IRepository
只是定义了"当使用字符串调用GetUserInfo时,它应该返回MyObject"的约定。 如何做到这一点并不重要。
相信你的直觉。我假设您的 DbContext 实现了一个接口,该接口允许您访问所需的所有内容,并且该接口是您通过 DI 传递到控制器构造函数中的接口。
在测试程序集中,创建一个实现接口并返回已知模拟数据的MockDbContext
。然后使用MockDbContext
创建控制器的实例,并针对控制器运行测试。
如果您有任何问题,请发表评论,我将尝试通过更新答案来提供帮助。