使用Xunit与Moq单元测试PersonService时Mock不返回预设值问题
问题原因
你的测试用例中Moq Setup未生效,返回默认值0是以下几个匹配规则不满足导致的:
- Transaction参数实例不匹配:业务代码中调用
PostPersonAsync时传入的是_unitOfWork.Transaction属性,而你测试用例中Setup时传入的是自己手动创建的_IDbTransaction.Object实例,两个实例不是同一个对象,Moq默认严格匹配参数引用,匹配失败就会返回类型默认值0。 - PersonDto参数引用不匹配:如果
PersonDto没有重写Equals方法,Moq会按引用匹配参数。你测试用例中提前Map出来的PersonDto,和业务代码运行时内部Map生成的PersonDto是两个不同的引用对象,也会导致匹配失败。 - 缺少必要的Mock配置:你没有配置
_unitOfWorkMock1的Begin()、Transaction、Connection等成员的返回值,容易触发空引用或者逻辑中断。
解决方案
按以下步骤修改测试用例即可:
- 配置
IUnitOfWork的Transaction属性,返回你创建的事务Mock实例,保证参数实例一致 - 调整
PostPersonAsync的Setup参数匹配规则,用It.Is<T>或者It.IsAny<T>替代固定实例匹配 - 测试方法改为异步写法,避免
.Result导致的死锁问题 - 补全必要的成员Mock配置,保证业务逻辑可以正常跑完
修改后的测试用例代码
[Fact] public async Task PostPersonAsync_Should_SavePerson() { //Arrange var dbTransactionMock = new Mock<IDbTransaction>(); var dbConnectionMock = new Mock<IDbConnection>(); var unitOfWorkMock = new Mock<IUnitOfWork>(); // 补全Connection属性Mock,避免using块报错 unitOfWorkMock.Setup(s => s.Connection).Returns(dbConnectionMock.Object); // 配置Transaction返回我们创建的事务实例,保证参数匹配 unitOfWorkMock.Setup(s => s.Transaction).Returns(dbTransactionMock.Object); var expectedAffectedRows = 5; // 参数用It.Is匹配,只要是符合预期的PersonDto即可,也可以加字段校验规则 unitOfWorkMock.Setup(s => s.PersonRepository.PostPersonAsync( It.Is<PersonDto>(d => d.Name == PersonServiceData.PersonModel.Name && d.Age == PersonServiceData.PersonModel.Age), // 按需补充你要校验的DTO字段 dbTransactionMock.Object)) .ReturnsAsync(expectedAffectedRows); // 补全其他方法的Mock配置 unitOfWorkMock.Setup(s => s.OpenConnection()); unitOfWorkMock.Setup(s => s.Begin()); unitOfWorkMock.Setup(s => s.Commit()); unitOfWorkMock.Setup(s => s.CloseConnection()); var target = new PersonService(unitOfWorkMock.Object, _mapper); //Act var result = await target.PostPersonAsync(PersonServiceData.PersonModel); //Assert Assert.Equal(expectedAffectedRows, result); // 可选补充校验:验证事务行为是否符合预期 unitOfWorkMock.Verify(s => s.Commit(), Times.Once); unitOfWorkMock.Verify(s => s.Rollback(), Times.Never); }
内容的提问来源于stack exchange,提问作者sarrow
相关产品推荐
相关产品推荐

