无法为 MediatR 设置起订量回调

Posted

技术标签:

【中文标题】无法为 MediatR 设置起订量回调【英文标题】:Cannot setup a Moq callback for MediatR 【发布时间】:2020-03-21 20:46:20 【问题描述】:

我正在编写一个测试来验证我的控制器是否使用预期的查询参数调用我的查询。这是我的查询类:

public class GetProducts : IRequest<IEnumerable<Product>>

    public int CategoryId  get; set; 

这实现了IRequest&lt;T&gt; MediatR 接口。

这是不起作用的测试用例:

[Theory]
[InlineData(1)]
public async Task GetProductsAsync_GivenValidRequestParameters_ReturnsListGetProductsResponseAsync(int categoryId)

    var expectedQuery = new GetProducts
    
        CategoryId = categoryId
    ;

    _mediatorMock
        .Setup(s => s.Send(It.IsAny<GetProducts>(), It.IsAny<CancellationToken>()))
        .Callback<GetProducts, CancellationToken>((query, ct) => query.Should().BeEquivalentTo(expectedQuery))
        .ReturnsAsync(Enumerable.Empty<Product>());

    var response = await _productsController.GetProductsAsync(categoryId);

    response.Result.Should().BeOfType<OkObjectResult>();
    _mediatorMock.Verify(s => s.Send(It.IsAny<GetProducts>(), It.IsAny<CancellationToken>()), Times.Once);

这是我正在测试的控制器:

[ApiController]
[Route("categories/categoryId:int/products")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[TrackUsage]
public class ProductsController : ControllerBase

    private readonly IMediator _mediator;

    public ProductsController(IMediator mediator)
    
        _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
    

    [HttpGet]
    public async Task<ActionResult<IEnumerable<GetProductsResponse>>> GetProductsAsync([FromRoute]int categoryId)
    
        var query = new GetProducts
        
            CategoryId = categoryId
        ;

        var products = await _mediator.Send(query);

        return Ok(products.ToResponse());
    

它抱怨是因为它找不到以&lt;GetProducts, CancellationToken&gt; 作为参数的回调,即使它看起来是正确的。

我知道我可以使用 It.Is&lt;...&gt;(callback =&gt; true) 来检查每个属性,但可能会有包含多个属性的查询,我更愿意使用 FluentAssertion 对其进行测试。

【问题讨论】:

【参考方案1】:

通用Send 定义

public interface IMediator

    /// <summary>
    /// Asynchronously send a request to a single handler
    /// </summary>
    /// <typeparam name="TResponse">Response type</typeparam>
    /// <param name="request">Request object</param>
    /// <param name="cancellationToken">Optional cancellation token</param>
    /// <returns>A task that represents the send operation. The task result contains the handler response</returns>
    Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);

    //...

Source

Callback 需要匹配提供的定义

//...

_mediatorMock
    .Setup(s => s.Send<IEnumerable<Product>>(It.IsAny<IRequest<IEnumerable<Product>>>(), It.IsAny<CancellationToken>()))
    .Callback<IRequest<IEnumerable<Product>>, CancellationToken>((query, ct) => 
        ((GetProducts)query).Should().BeEquivalentTo(expectedQuery)
    )
    .ReturnsAsync(Enumerable.Empty<Product>());

//...

【讨论】:

public class GetProducts : IRequest&lt;IEnumerable&lt;Product&gt;&gt; 但我的班级确实实现了这一点。 C#(或者更确切地说是 Moq)无法检测到这一点? 确实有效。我现在很好奇为什么如果我提供GetProducts 而不是IRequest&lt;IEnumerable&lt;Product&gt; 它不起作用。但这是一个不同的问题。谢谢。

以上是关于无法为 MediatR 设置起订量回调的主要内容,如果未能解决你的问题,请参考以下文章

最小起订量中的设置序列

起订量和设置数据库上下文

起订量 IServiceProvider / IServiceScope

使用最小起订量模拟静态属性

使用最小起订量测试 Polly 重试策略

起订量中 VerifyAll() 的用途是啥?