Moq传入不同值给Returns却返回相同结果的问题排查
问题排查与解决
嘿,我仔细梳理了你的测试代码和相关实现,找到问题的核心原因了!
核心问题:Moq参数匹配与实际调用不匹配
你在Mock IDbConnectionExtensionsWrapper.Get方法时,Setup的参数匹配逻辑写错了:
// 你原来的写法 extensionWrapper .Setup(ex => ex.Get<User>(It.IsAny<IDbConnection>(), It.IsAny<IDbTransaction>(), It.IsAny<int>())) .Returns(Task.FromResult(users));
但看你的仓储代码,调用Get时只传了第一个参数,后两个参数用的是默认值null:
var users = await this.ConnectionWrapper .Get<User>(this.Connection);
再对照IDbConnectionExtensionsWrapper.Get的定义,后两个参数是可空类型:
public async Task<IEnumerable<T>> Get<T>( IDbConnection connection, IDbTransaction? transaction = null, int? timeout = null)
你用It.IsAny<int>()匹配的是非空int值,但实际传递的是int?类型的null,两者完全不匹配!第一个测试用例成功可能是偶然情况,后续测试因为参数不匹配,Moq返回了默认值(空枚举)。
修复方案
把Setup里的参数匹配改成对应可空类型的匹配即可,两种方式任选:
方式1:匹配任意可空值(更灵活)
extensionWrapper .Setup(ex => ex.Get<User>( It.IsAny<IDbConnection>(), It.IsAny<IDbTransaction?>(), // 匹配可空的事务参数 It.IsAny<int?>())) // 匹配可空的超时参数 .Returns(Task.FromResult(users));
方式2:精准匹配null(更贴合你的仓储调用逻辑)
extensionWrapper .Setup(ex => ex.Get<User>( It.IsAny<IDbConnection>(), null, null)) .Returns(Task.FromResult(users));
额外验证小技巧
可以在每个测试用例末尾添加Verify断言,确认Mock的方法确实被调用了,方便快速排查参数匹配问题:
extensionWrapper.Verify( ex => ex.Get<User>(It.IsAny<IDbConnection>(), null, null), Times.Once, "Get方法应该被调用一次");
小优化建议
- 你的
GenerateUsers方法里,所有用户的Salt和Hash都是同一个值,把salt的生成逻辑放到循环内部,让每个用户拥有独立的salt会更合理:
private static IEnumerable<User> GenerateUsers(int count) { var random = new Random(); var users = new List<User>(); for (var i = 0; i < count; i++) { using var crypto = new RNGCryptoServiceProvider(); var salt = new byte[16]; crypto.GetBytes(salt); users.Add(new User { Id = i, Username = Convert.ToString(i), Salt = salt, Hash = salt, }); } return users; }
- xUnit中
Assert.AreEqual可以换成更简洁的Assert.Equal,功能完全一致:
Assert.Equal(userCount, usersFromRepository.Count());
内容的提问来源于stack exchange,提问作者Liam Mueller
相关产品推荐
相关产品推荐

