我正在尝试测试我制作的WebApi控制器。我试图使用依赖注入来使测试更容易。虽然它实际上有相反的效果。
我目前有一个控制器在其构造函数中使用repo接口。 repo接口也在其构造函数中使用DbContext接口。我是否正确地认为我需要模拟DbContext,并在模拟repo时将该模拟的上下文作为参数传递,然后将该模拟的repo传递给我正在测试的控制器的实际实现?
我正在使用Moq和NUnit。
由于
答案 0 :(得分:4)
我假设您正在谈论单元测试,因为您正在使用模拟。
您不需要比第一级接口更深入地模拟您单元测试所依赖的类。在您的示例中,您的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
});
}
}
在上面的测试中,我们提供了IRepository
在调用GetUserInformation
时应返回的示例数据,我们不依赖于实际DbContext
或IDbContext
甚至,因为IRepository
只是定义了一个&#34;当用字符串调用GetUserInformation时,它应该返回一个MyObject&#34;。如何做到这一点并不重要。
答案 1 :(得分:1)
相信你的直觉。我假设您的DbContext实现了一个接口,使您可以访问所需的一切,并且该接口是通过DI传递到控制器构造函数的接口。
在测试程序集中,创建一个实现接口的MockDbContext
并返回已知的模拟数据。然后使用MockDbContext
创建控制器的实例,并对控制器运行测试。
如果您有任何问题,请发表评论,我会尝试通过更新答案来提供帮助。