如何使用 Moq 创建私有依赖方法设置

How to create private dependency method setup using Moq

我有以下 Controller 接口:

public interface IInformationController
{
  string GetStoredInformation();
  string GetInformation();
}

控制器 class 如下:

public class InformationController : ControllerBase, IInformationController
{
  private InformationProvider1 InformationProvider1;
  private InformationProvider2 InformationProvider2;
  private IBasicRepository repository;

  public InformationController(IBasicRepository basicRepository)  
    => repository = basicRepository;

  public string GetStoredInformation()
    => repository.GetStoredInformation();

  public string GetInformation()
    => $"Information is {informationProvider1.GetInformationHeader()}, Information detail is {informationProvider2.GetInformationDetail()}";
  
}

我打算使用 xUnit 和 MoqInformationController 创建一个单元测试。

这是我的测试class:

public class InformationControllerTest
{
  public InformationControllerTest()
  {
    repositoryMock = new Mock<IBasicRepository>();
    repositoryMock.Setup(repository => repository.GetStoredInformation()).Returns("Stored information");
    SUT = new InformationController(repositoryMock.Object);
  }

  [Fact]
  public void GetStoredInformation_Returns_Stored_Information()
  {
    string result = SUT.GetStoredInformation();
    Assert.Equal("Stored information", result);
  }

  [Fact]
  public void GetInformation_Returns_Valid_Information()
  {
    string result = SUT.GetInformation(); //TODO: how to avoid the usage of actual provider implementations?
    Assert.Equal("Information is Header1, Information detail is Detail1", result);
  }
}

如您所见,有私人提供者。不幸的是,它们是外部依赖项,无法通过依赖注入轻松引入。

我应该如何模拟他们的实际通话?我应该模拟控制器方法(哪个恕我直言应该使测试实际控制器的过程无效)?我应该尝试通过依赖注入来获取它们吗?

单元测试暴露了您当前设计的缺点,它与外部依赖项或实现问题紧密耦合。

依赖项应该明确地注入到它们的依赖项中

Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.

引用Explicit Dependencies Principle

public class InformationController : ControllerBase, IInformationController {
    private IInformationProvider1 informationProvider1;
    private IInformationProvider2 informationProvider2;
    private IBasicRepository repository;

    public InformationController(IBasicRepository basicRepository,
        IInformationProvider1 informationProvider1, 
        IInformationProvider2 informationProvider2) {
        repository = basicRepository;
        this.informationProvider1 = informationProvider1;
        this.informationProvider2 = informationProvider2
    }

    public string GetStoredInformation()
        => repository.GetStoredInformation();

    public string GetInformation()
        => $"Information is {informationProvider1.GetInformationHeader()}, Information detail is {informationProvider2.GetInformationDetail()}";
  
    //...
}

为这些外部依赖项创建抽象和实现,然后将它们注册到 DI 容器,以便它们可以在 运行 时解析并正确提供给控制器。现在这将允许它们也被隔离测试,而不会从它们的实际实现中产生不良副作用。