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

使用Moq测试Entity Framework:单元测试中DbSet.Add不生效

问题根源

你的核心问题是Mock配置与被测试代码的调用逻辑不匹配:

  • 被测试方法中调用的是context.Roles.Add(rol)(DbSet<T>的Add方法),但你仅Mock了DbContext.Add方法的回调,导致新增操作根本没触发你写的List.Add逻辑。
  • Mock的DbSet默认不支持修改操作,且未模拟SaveChangesAsync的主键生成行为,导致新增对象的RolId不会自动赋值。
修复步骤

1. 重构Mock DbSet配置,支持Add操作

需要给Mock的DbSet配置Add方法的回调,让新增对象同步到后台的roles列表,同时保证AsQueryable()能实时反映列表变化:

public static void SetupDbContextData(this Mock<DbContext> mockContext)
{
    var roles = new List<Rol>
    {
        new Rol { RolId = 1, Nombre = "Consumidor", tipo_de_usuario = 3 },
        new Rol { RolId = 2, Nombre = "Proveedor", tipo_de_usuario = 2 },
        new Rol { RolId = 3, Nombre = "Administrador", tipo_de_usuario = 3 }
    };

    // 创建可修改的Mock DbSet
    var mockRolesSet = roles.AsQueryable().BuildMockDbSet();

    // 配置DbSet的Add方法,同步到后台列表
    mockRolesSet.Setup(set => set.Add(It.IsAny<Rol>()))
                .Callback<Rol>(rol => roles.Add(rol));

    // 若代码中使用AddAsync,需补充配置
    mockRolesSet.Setup(set => set.AddAsync(It.IsAny<Rol>(), It.IsAny<CancellationToken>()))
                .ReturnsAsync((Rol rol, CancellationToken _) => 
                {
                    roles.Add(rol);
                    return rol;
                });

    // 将配置好的DbSet绑定到Context的Roles属性
    mockContext.Setup(x => x.Roles).Returns(mockRolesSet.Object);

    // 原DbContext.Add的配置可删除,因为被测试代码未调用该方法
}

2. Mock SaveChangesAsync,模拟主键生成

真实环境中EF会自动生成主键,测试时需手动模拟该行为:

// 在SetupDbContextData方法中添加以下配置
mockContext.Setup(d => d.SaveChangesAsync(It.IsAny<CancellationToken>()))
            .ReturnsAsync(() => 
            {
                // 给未赋值RolId的新增对象分配自增Id
                var newRoles = roles.Where(r => r.RolId == 0).ToList();
                foreach (var rol in newRoles)
                {
                    rol.RolId = roles.Max(r => r.RolId) + 1;
                }
                return newRoles.Count; // 返回受影响行数
            });

3. 优化单元测试(可选)

将测试方法返回类型改为Task(XUnit原生支持异步测试返回Task),并优化断言精度:

[Fact]
public async Task InsertarRolAsyncTest()
{
    var insert = new Rol
    {
        Nombre = "RolInsert",
        tipo_de_usuario = 1
    };
                      
    var valores = await _handler.insertarRolAsync(insert, _handler._context, CancellationToken.None);
    output.WriteLine("Valores:" + valores);

    Assert.IsType<int>(valores);
    Assert.NotEqual(99, valores);
    Assert.Equal(4, valores); // 原有最大Id为3,新增对象Id应为4
}
验证逻辑

调整后执行测试时:

  1. context.Roles.Add(rol)触发Mock DbSet的Add回调,将对象加入roles列表
  2. SaveChangesAsync被调用时,自动为新增对象分配RolId=4
  3. context.Roles.Last().RolId获取到新增对象的Id,断言通过

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 19:39:54