模拟 ApiController

Mocking ApiController

我正在尝试测试我制作的 WebApi 控制器。我尝试使用依赖注入来简化测试。虽然它实际上有相反的效果。

我目前有一个控制器在其构造函数中采用 repo 接口。 repo 接口也在其构造函数中采用 DbContext 接口。我是否正确地认为我需要模拟 DbContext,并在模拟回购时将该模拟上下文作为参数传递,然后将该模拟回购传递到我正在测试的控制器的实际实现中?

我正在使用 Moq 和 NUnit。

谢谢

相信你的直觉。我假设您的 DbContext 实现了一个接口,使您可以访问所需的一切,并且该接口是您通过 DI 传递给控制器​​构造函数的内容。

在您的测试程序集中,创建一个 MockDbContext 实现接口和 returns 已知模拟数据。然后使用 MockDbContext 和 运行 针对控制器的测试创建控制器实例。

如果您有任何问题,请发表评论,我会尽力通过更新答案来提供帮助。

我假设你在谈论单元测试,因为你正在使用模拟。

您不需要比单元测试所依赖的 class 第一级接口更深入地模拟。在您的示例中,您的 controller 依赖于我们调用的接口 IRepositoryIRepository 实现 又包含 IDbContext。请注意上一句中的 bold/italics,只要您模拟的接口是 IRepository,那么 IDbContext 就没有任何关联 - IDbContext 是一个依赖项你的具体存储库,而不是你的 IRepository.

你的 IRepository 应该有你的控制器模拟 data/behavior 与单元测试你的控制器相关的一切。

示例:

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,您依赖接口 IRepositoryIRepository (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 时应该 return 的示例数据,我们不依赖于实际的 DbContext,或者IDbContext 甚至,因为 IRepository 只是定义了 "when GetUserInformation is called with a string, it should return a MyObject" 的合同。如何做到这一点并不重要。