使用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 }
验证逻辑
调整后执行测试时:
context.Roles.Add(rol)触发Mock DbSet的Add回调,将对象加入roles列表SaveChangesAsync被调用时,自动为新增对象分配RolId=4context.Roles.Last().RolId获取到新增对象的Id,断言通过
内容的提问来源于stack exchange,提问作者blondiedies
相关产品推荐
相关产品推荐

