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

如何使用xUnit结合Moq编写Put方法单元测试?

使用xUnit + Moq测试Put方法的Update逻辑

问题场景

我想用xUnit测试Put方法对应的Update逻辑,现有代码如下:

控制器Put方法

[HttpPut]
[Route("{id}")]
public IActionResult Put([FromBody]BookDto book, [FromRoute]int id)
{
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }
    var isUpdated = _service.Update(book);
    if (!isUpdated)
    {
        return NotFound();
    }
    return Ok();
}

BookService的Update方法

public bool Update(BookDto book)
{
    var bookDb = _context.Books.FirstOrDefault(x => x.Id == book.Id);
    if(bookDb == null)
    {
        return false;
    }
    bookDb.Title = book.Title;
    _context.SaveChanges();
    var existingAuthors = _context.Book_Authors.Where(x => x.BookId == book.Id).ToList();
    _context.Book_Authors.RemoveRange(existingAuthors);
    _context.SaveChanges();

    foreach (var AuthorsId in book.AuthorsId)
    {
        var newBookAuthors = new Book_Author()
        {
            BookId = book.Id,
            AuthorId = AuthorsId
        };
        _context.Book_Authors.Add(newBookAuthors);
    }
    _context.SaveChanges();

    return true;
}

BookDto定义

public class BookDto
{
    public int Id { get; set; }
    public string Title { get; set; }
    public List<int> AuthorsId { get; set; }
}

请问如何用Moq编写Put方法的测试用例?


测试实现步骤

首先确保你的BookService是基于接口(比如IBookService)实现的,这样才能用Moq进行模拟。如果还没定义接口,先补一个:

public interface IBookService
{
    bool Update(BookDto book);
}

然后编写xUnit测试类,核心思路是用Moq模拟IBookService,注入到控制器中,针对不同场景验证返回结果。

1. 测试类基础结构

using Xunit;
using Moq;
using Microsoft.AspNetCore.Mvc;
// 替换成你的项目命名空间
using YourNamespace.Controllers;
using YourNamespace.Services;
using YourNamespace.Dtos;

public class BooksControllerTests
{
    private readonly Mock<IBookService> _mockBookService;
    private readonly BooksController _controller;

    public BooksControllerTests()
    {
        // 初始化Mock服务实例
        _mockBookService = new Mock<IBookService>();
        // 将Mock服务注入控制器
        _controller = new BooksController(_mockBookService.Object);
    }
}

2. 测试场景1:ModelState无效时返回BadRequest

当请求的BookDto不符合验证规则(比如Title为空),控制器应返回BadRequestResult。

[Fact]
public void Put_InvalidModelState_ReturnsBadRequest()
{
    // Arrange
    // 手动添加ModelState错误,模拟参数校验失败
    _controller.ModelState.AddModelError("Title", "Title is required");
    var invalidBookDto = new BookDto { Id = 1, Title = "", AuthorsId = new List<int>() };

    // Act
    var result = _controller.Put(invalidBookDto, 1);

    // Assert
    Assert.IsType<BadRequestResult>(result);
}

3. 测试场景2:Update失败(书籍不存在)时返回NotFound

当IBookService.Update返回false(表示找不到对应书籍),控制器应返回NotFoundResult。

[Fact]
public void Put_UpdateFails_ReturnsNotFound()
{
    // Arrange
    var bookDto = new BookDto { Id = 1, Title = "Updated Title", AuthorsId = new List<int> { 1, 2 } };
    // 配置Mock服务:调用Update时返回false
    _mockBookService.Setup(s => s.Update(bookDto)).Returns(false);

    // Act
    var result = _controller.Put(bookDto, 1);

    // Assert
    Assert.IsType<NotFoundResult>(result);
    // 验证Update方法是否被调用了一次
    _mockBookService.Verify(s => s.Update(bookDto), Times.Once);
}

4. 测试场景3:Update成功时返回Ok

当IBookService.Update返回true,控制器应返回OkResult。

[Fact]
public void Put_UpdateSucceeds_ReturnsOk()
{
    // Arrange
    var bookDto = new BookDto { Id = 1, Title = "Updated Title", AuthorsId = new List<int> { 1, 2 } };
    // 配置Mock服务:调用Update时返回true
    _mockBookService.Setup(s => s.Update(bookDto)).Returns(true);

    // Act
    var result = _controller.Put(bookDto, 1);

    // Assert
    Assert.IsType<OkResult>(result);
    // 验证Update方法是否被正确调用
    _mockBookService.Verify(s => s.Update(bookDto), Times.Once);
}

额外说明

  • 如果需要测试BookService.Update的内部数据库操作逻辑,建议使用Microsoft.EntityFrameworkCore.InMemory内存数据库模拟DbContext,而非Mock服务;但如果仅测试控制器层的分支逻辑,Mock服务已足够覆盖。
  • 若业务需要校验book.Id与路由参数id的一致性,可在控制器中补充校验逻辑后,新增对应测试场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:02:17