如何用AutoFixture模拟返回模型?xUnit测试断言失败排查
问题排查与修复方案
1. 错误1:Mock验证调用次数失败(Expected invocation once, but was 0 times)
问题原因
验证语句中使用了错误的参数类型:
dbRepoMock.Verify(x => x.GetBy(v => v.Id == It.IsAny<string>()), Times.Once);
实际被测试方法中Id是int类型,但这里用了It.IsAny<string>(),导致Moq无法匹配到实际的调用,认为Mock从未被调用。
修复方案
将验证语句中的It.IsAny<string>()改为匹配逻辑更精准的表达式验证(推荐):
// 验证表达式确实是用来筛选目标user的 dbRepoMock.Verify(x => x.GetBy(It.Is<Expression<Func<User, bool>>>(expr => expr.Compile()(user))), Times.Once);
2. 错误2:Assert.Equal返回false,GetBy返回的模型Id与预设不一致
问题原因
Moq对Expression<Func<T, bool>>类型的参数默认按引用比较,而非逻辑比较。你的Setup语句:
dbRepoMock.Setup(x => x.GetBy(v => v.Id == It.IsAny<int>())).ReturnsAsync(user);
这里的表达式与被测试方法中实际传入的x => x.Id == Id不是同一个实例,导致Mock不会返回预设的user,而是返回默认值(或AutoFixture生成的其他实例),最终response.Id与预期的id不匹配。
修复方案
修改Setup,让Moq识别表达式的逻辑而非引用:
// 匹配任何能正确筛选出目标user的表达式 dbRepoMock.Setup(x => x.GetBy(It.Is<Expression<Func<User, bool>>>(expr => expr.Compile()(user)))).ReturnsAsync(user);
3. 额外的潜在问题
测试方法返回类型错误
xUnit中异步测试应返回Task而非void,否则测试无法正确等待异步操作完成,可能导致不稳定:
// 原代码 public async void GetById_ReturnSuccess(...) // 修改为 public async Task GetById_ReturnSuccess(...)
被测试方法返回类型不匹配
被测试方法声明返回GeolocationDataResponse,但实际返回UserResponse,这会导致编译错误,需修正返回类型:
// 原代码 public async Task<GeolocationDataResponse> GetById(int Id) // 修改为(假设正确返回类型是UserResponse) public async Task<UserResponse> GetById(int Id)
修改后的完整测试代码示例
public async Task<UserResponse> GetById(int Id) { var user = await _dbRepository.GetBy(x => x.Id == Id); if (user is null) return null; var userResponse = _mapper.Map<UserResponse>(user); return userResponse; } public class UserProviderTests { [Theory, UserProviderTestsData] public async Task GetById_ReturnSuccess( User user, [Frozen] Mock<IDbRepository<User>> dbRepoMock, UserProvider sut ) { int id = user.Id; // 修正Setup的表达式匹配逻辑 dbRepoMock.Setup(x => x.GetBy(It.Is<Expression<Func<User, bool>>>(expr => expr.Compile()(user)))).ReturnsAsync(user); var response = await sut.GetById(id); Assert.Equal(response.Id, id); // 修正Verify的表达式匹配逻辑 dbRepoMock.Verify(x => x.GetBy(It.Is<Expression<Func<User, bool>>>(expr => expr.Compile()(user))), Times.Once); } internal class UserProviderTestsDataAttribute : AutoDataAttribute { public UserProviderTestsDataAttribute() : base( () => { var fixture = new Fixture(); fixture.Customize(new AutoMoqCustomization() { ConfigureMembers = true }); fixture.Customize(new UserProviderTestsCustomization()); return fixture; }) { } } internal class UserProviderTestsCustomization : ICustomization { public void Customize(IFixture fixture) { fixture.Register(() => new MapperConfiguration(cfg => { cfg.AddProfile(new UserAutomapper()); }).CreateMapper()); } } }
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

