You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Moq报错:期望调用1次实际0次,Post接口测试失败但接口正常

问题:Post端点单元测试Moq验证失败,提示调用次数为0

我为Get和Post两个端点编写了相似的单元测试,用于验证端点被访问时对应的服务方法是否被调用。其中Get测试可正常通过,但Post测试持续出现Moq错误**"Moq expected invocation once, but was 0times"**,尽管实际端点运行正常。

Post端点代码

[HttpPost("api")]
public async Task<IActionResult> PostVenda(PostVendaRequestModel postRequest) {
    Venda postVenda = await _vendaService.PostVendaService(
        vendedor: postRequest.vendedor, 
        carrinho: postRequest.carrinho);

    return Ok(postVenda);
}

Get测试代码(可正常通过)

[Fact]
public async Task Get_OnSuccess_InvokesVendaServiceOnce() {
    //Arrange
    var mockVendaService = new Mock<IVendaService>();
    
    mockVendaService
        .Setup(service => service.GetVendaByIdService(1))
        .ReturnsAsync(new Venda());
                                                            
    var sut = new VendaController(mockVendaService.Object);

    //Act
    var result = await sut.GetVendaById(1);

    //Assert
    mockVendaService.Verify(
        service => service.GetVendaByIdService(1), 
        Times.Once()
    );
}

Post测试代码(失败)

[Fact]
public async Task Post_Venda_OnSuccess_InvokesVendaServiceOnce() {
    //Arrange
    var mockVendaService = new Mock<IVendaService>();
    VendaController.PostVendaRequestModel newVenda = new VendaController.PostVendaRequestModel() { 
        carrinho = CarrinhoFixture.Carrinho, 
        vendedor = VendedorFixture.Vendedor 
    };

    mockVendaService
        .Setup(service => service.PostVendaService(VendedorFixture.Vendedor, CarrinhoFixture.Carrinho))
        .ReturnsAsync(new Venda());

    var sut = new VendaController(mockVendaService.Object);


    //Act
    var result = await sut.PostVenda(new VendaController.PostVendaRequestModel());

    //Assert
    mockVendaService.Verify(
        service => service.PostVendaService(VendedorFixture.Vendedor, CarrinhoFixture.Carrinho), 
        Times.Once);
}

VendaService代码

public class VendaService : IVendaService {
    public List<Venda> _vendas;

    public VendaService() {
        this._vendas = VendasFixture.Vendas;
    }
    public async Task<Venda> GetVendaByIdService(int id) {
        List<Venda> vendaSearch = _vendas.Where(v => v.VendaId == id).ToList();
        if(vendaSearch.Count == 0) 
            return null;

        return vendaSearch[0];
    }

    public async Task<Venda> PostVendaService(Vendedor vendedor, List<Item> carrinho) {
        Venda venda = new Venda() {
            VendaId = 1,
            Vendedor = vendedor,
            Carrinho = carrinho,
            DataDaVenda = DateTime.Now,
            Status = Enums.Status.AguardandoPagamento
        };
        VendasFixture.Vendas.Add(venda);
        return venda;
    }
}

环境信息

  • .Net Core 6.0.402
  • 使用的NuGet包:Moq 4.18.2、FluentAssertions 6.7.0、xunit 2.4.1

问题原因及解决方法

核心问题

Post测试的Act步骤传入了空的请求对象,导致控制器调用PostVendaService时传入的vendedor和carrinho都是null,和Setup中指定的VendedorFixture.Vendedor、CarrinhoFixture.Carrinho不匹配,Moq无法识别这次调用,因此验证失败。

另外,Verify中的Times.Once缺少括号,正确写法是Times.Once(),不过这不是主要报错原因。

修复后的Post测试代码

[Fact]
public async Task Post_Venda_OnSuccess_InvokesVendaServiceOnce() {
    //Arrange
    var mockVendaService = new Mock<IVendaService>();
    var newVenda = new VendaController.PostVendaRequestModel() { 
        carrinho = CarrinhoFixture.Carrinho, 
        vendedor = VendedorFixture.Vendedor 
    };

    mockVendaService
        .Setup(service => service.PostVendaService(VendedorFixture.Vendedor, CarrinhoFixture.Carrinho))
        .ReturnsAsync(new Venda());

    var sut = new VendaController(mockVendaService.Object);

    //Act - 使用预先创建好的newVenda对象,而非空对象
    var result = await sut.PostVenda(newVenda);

    //Assert - 修正Times.Once为Times.Once()
    mockVendaService.Verify(
        service => service.PostVendaService(VendedorFixture.Vendedor, CarrinhoFixture.Carrinho), 
        Times.Once());
}

可选优化:使用参数匹配器增强灵活性

如果不需要严格匹配Fixture对象,也可以用It.IsAny<T>来匹配任意参数,这样测试更灵活:

// Setup时
mockVendaService
    .Setup(service => service.PostVendaService(It.IsAny<Vendedor>(), It.IsAny<List<Item>>()))
    .ReturnsAsync(new Venda());

// Verify时
mockVendaService.Verify(
    service => service.PostVendaService(It.IsAny<Vendedor>(), It.IsAny<List<Item>>()), 
    Times.Once());

内容的提问来源于stack exchange,提问作者Yuri Soares

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:05:27