如何使用 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 和 Moq 为 InformationController
创建一个单元测试。
这是我的测试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 容器,以便它们可以在 运行 时解析并正确提供给控制器。现在这将允许它们也被隔离测试,而不会从它们的实际实现中产生不良副作用。
我有以下 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 和 Moq 为 InformationController
创建一个单元测试。
这是我的测试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 容器,以便它们可以在 运行 时解析并正确提供给控制器。现在这将允许它们也被隔离测试,而不会从它们的实际实现中产生不良副作用。