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
相关产品推荐
相关产品推荐

