如何处理 ASP.NET Core 中包含两个构造函数的 Controller?

问题描述 投票:0回答:2

我有一个 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# unit-testing dependency-injection constructor
2个回答
0
投票

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

这样框架就会知道使用哪个构造函数,因为只有一个。每当您需要添加新的依赖项时,请将该依赖项添加到构造函数中,而不是仅为该依赖项添加全新的构造函数。


0
投票

将所有依赖项移至单个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);
}
© www.soinside.com 2019 - 2024. All rights reserved.