【问题标题】:How to create private dependency method setup using Moq如何使用 Moq 创建私有依赖方法设置
【发布时间】:2020-11-23 15:20:25
【问题描述】:

我有以下Controller接口:

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

控制器类如下:

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 创建一个单元测试。

这是我的测试课:

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);
  }
}

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

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

【问题讨论】:

  • 这些提供程序是如何初始化或注入控制器的?提供的示例中没有显示?
  • 单元测试暴露了你当前设计的缺点。依赖项应该显式注入到它们的依赖项中。
  • 我的想法完全一样,“我如何嘲笑这个私人的东西?” = 这个类不可单元测试

标签: c# moq xunit


【解决方案1】:

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

依赖项应该显式注入到它们的依赖项中

方法和类应该明确要求(通常通过方法参数或构造函数参数)它们需要的任何协作对象才能正常运行。

参考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 容器,以便它们可以在运行时被解析并正确提供给控制器。现在,这将允许它们也可以单独进行测试,而不会对其实际实现产生不良副作用。

【讨论】:

  • 感谢您的澄清,我错过了明显的解释:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多