我有一个 API,我打算通过 xUnit 和 Moq 进行单元测试。它实际上有一个构造函数,但我必须根据 Visual Studio 的建议添加另一个构造函数以用于测试的特定目的。它非常适合测试,但是当我尝试通过 Swagger 测试 API 时,我遇到了另一个错误。 “System.InvalidOperationException:在类型“Product.API.Controllers.ProductController”中找到了接受所有给定参数类型的多个构造函数。应该只有一个适用的构造函数”。 以下是控制器的内容
public class ProductController : ControllerBase
{
private readonly IMediator _mediator;
private readonly IConfiguration _configuration;
private readonly UserManager<User> _userManager;
private readonly SignInManager<User> _signInManager;
private readonly ProductDbContext _productDbContext;
private readonly IUserService _userService;
public ProductController(IConfiguration configuration, UserManager<User> userManager, ProductDbContext productDbContext
, SignInManager<User> signInManager, IUserService userService)
{
_configuration = configuration;
_userManager = userManager;
_productDbContext = productDbContext;
_signInManager = signInManager;
_userService = userService;
}
public ProductController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> GetAllProducts()
{
var products = await _mediator.Send(new GetAllProductsQuery());
return Ok(products);
}
下面是单元测试的代码
private readonly Mock<IMediator> _mediatorMock;
private readonly ProductController _controller;
public ProductControllerTest()
{
_mediatorMock = new Mock<IMediator>();
_controller = new ProductController(_mediatorMock.Object);
}
[Fact]
public async Task ProductController_GetAllProducts_ShouldReturnStatusCode200()
{
//arrange
var expected = new List<ProductModelDTO>();
_mediatorMock.Setup(m=> m.Send(It.IsAny<GetAllProductsQuery>(), default(CancellationToken))).ReturnsAsync(expected);
//act
var result = await _controller.GetAllProducts();
//assert
var okResult = result as OkObjectResult;
okResult?.StatusCode.Should().Be(200);
okResult?.Value.Should().BeOfType<List<ProductModelDTO>>();
}
我找到了一个可能的解决方案;将 [FromServices] 添加到第二个构造函数。但我遇到了同样的错误!
在 C# 语言级别,多个构造函数没有任何问题。然而,在 framework 级别(ASP.NET 中使用的依赖注入器尝试自动创建控制器的实例),多个构造函数会混淆框架。
使用一个构造函数。
如果您的控制器有这些依赖项:
private readonly IMediator _mediator;
private readonly IConfiguration _configuration;
private readonly UserManager<User> _userManager;
private readonly SignInManager<User> _signInManager;
private readonly ProductDbContext _productDbContext;
private readonly IUserService _userService;
然后将它们全部放入构造函数中:
public ProductController(
IConfiguration configuration,
UserManager<User> userManager,
ProductDbContext productDbContext,
SignInManager<User> signInManager,
IUserService userService,
IMediator mediator)
{
_configuration = configuration;
_userManager = userManager;
_productDbContext = productDbContext;
_signInManager = signInManager;
_userService = userService;
_mediator = mediator;
}
这样框架就会知道使用哪个构造函数,因为只有一个。每当您需要添加新的依赖项时,请将该依赖项添加到构造函数中,而不是仅为该依赖项添加全新的构造函数。
将所有依赖项移至单个ctor:
public ProductController(IConfiguration configuration,
UserManager<User> userManager,
ProductDbContext productDbContext,
SignInManager<User> signInManager,
IUserService userService,
IMediator mediator)
{
// ...
}
然后在测试中将所有值传递给它:
public ProductControllerTest()
{
_mediatorMock = new Mock<IMediator>();
_controller = new ProductController(..., _mediatorMock.Object); // mock everything else
}
或者,您可以使用 ASP.NET Core 功能来使用方法注入:
public ProductController(IConfiguration configuration, UserManager<User> userManager, ProductDbContext productDbContext
, SignInManager<User> signInManager, IUserService userService)
{
_configuration = configuration;
_userManager = userManager;
_productDbContext = productDbContext;
_signInManager = signInManager;
_userService = userService;
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> GetAllProducts([FromServices]IMediator mediator)
{
var products = await _mediator.Send(new GetAllProductsQuery());
return Ok(products);
}