Moq 枚举在 FirstOrDefault 上消失
Moq enumeration disappearing on FirstOrDefault
我编写了一个测试来验证一小部分新功能,该功能调用一个大型中间件函数,其中包含大量逻辑,可将一种类型的用户(ASP 网络成员用户)转换为一个内部的(SystemUser)。这是一个混乱的系统,但我无法将其删除,因此进行了新的测试。
我的测试调用了一个 GetAllActiveUsers()
方法,该方法向下调用几层并最终到达导致问题的枚举。该测试有一个模拟的 entity framework datacontext Mock<localEntities> mockDb
,其设置如下:
[SetUp]
public void Setup()
{
var mockUserSet = new Mock<DbSet<User>>();
this.mockDb = new Mock<localEntities>();
this.mockMembershipService = new Mock<IMembershipService>();
this.mockRoleService = new Mock<IRoleService>();
this.mockUserAgreementRelation = new Mock<DbSet<UserAgreementRelation>>();
this.testUsers = this.CreateSystemUsers();
this.mockDb.Setup(m => m.User).Returns(mockUserSet.Object);
mockUserAgreementRelation.SetupAllProperties();
var agreementRelations = this.testUsers.Select(u => new UserAgreementRelation() { AgentUserID = u.UserId, IsDeleted = null });
mockUserAgreementRelation.As<IQueryable<UserAgreementRelation>>().Setup(ua => ua.GetEnumerator()).Returns(agreementRelations.GetEnumerator());
this.mockDb.Setup(m => m.UserAgreementRelation).Returns(mockUserAgreementRelation.Object);
}
在我的测试中,我设置了一些用户并调用了 var testActiveUsers = testRepo.GetAllActiveUsers();
调用问题行。
这是我感到困惑的地方。
测试通过此代码:
var agreementRelations = databaseContext.UserAgreementRelation().ToList();
var userAgreementFromDB = agreementRelations
.FirstOrDefault(x => x.AgentUserID == ramsUser.UserId && (x.IsDeleted == null || !(bool)x.IsDeleted));
但此代码失败:
var userAgreementFromDB = databaseContext.UserAgreementRelation
.FirstOrDefault(x => x.AgentUserID == ramsUser.UserId && (x.IsDeleted == null || !(bool)x.IsDeleted));
此外,如果我调试并单步执行代码,我可以快速查看模拟的 UserAgreementRelation once。第一次我将看到我正在测试的每个用户的 UserAgreementRelation。如果我 运行 再次观察,即使代码没有推进,枚举也不会产生任何结果。我猜这与问题有关。但是,由于我不知道为什么会发生这种情况,所以我不能说它是如何导致这个问题的。
请记住,更简单的代码(没有 ToList())是生产代码。它目前运行良好,但我的测试失败了。由于这是一个直接的数据库调用,枚举整个 UserAgreementRelation table 不是一个可行的解决方案。
建议?
我一直用 Where 来做这样的事情,试试这个。
var userAgreementFromDB = databaseContext.UserAgreementRelation
.Where(x => x.AgentUserID == ramsUser.UserId && (x.IsDeleted == null || !(bool)x.IsDeleted)).FirstOrDefault();
我不确定我是否理解了问题,因为你没有详细说明它是如何发生的 "fails"。但也许你应该更改部分:
.Setup(ua => ua.GetEnumerator()).Returns(agreementRelations.GetEnumerator())
进入:
.Setup(ua => ua.GetEnumerator()).Returns(() => agreementRelations.GetEnumerator())
原因是如果你测试的代码调用GetEnumerator()
不止一次,Moq不应该交出旧的 "used" 枚举器实例,它可能已经推进到末尾并且可能已被处置。相反,Moq 应该重新 运行 一个 Func<>
,我的箭头 () => agreementRelations.GetEnumerator()
,以获得一个尚未推进 (MoveNext()
) 或关闭 ([=16=) 的新枚举器]).
提问者告诉我(在下面的评论中)他还需要 Setup
IQueryable
的属性 Provider
and Expression
。
我编写了一个测试来验证一小部分新功能,该功能调用一个大型中间件函数,其中包含大量逻辑,可将一种类型的用户(ASP 网络成员用户)转换为一个内部的(SystemUser)。这是一个混乱的系统,但我无法将其删除,因此进行了新的测试。
我的测试调用了一个 GetAllActiveUsers()
方法,该方法向下调用几层并最终到达导致问题的枚举。该测试有一个模拟的 entity framework datacontext Mock<localEntities> mockDb
,其设置如下:
[SetUp]
public void Setup()
{
var mockUserSet = new Mock<DbSet<User>>();
this.mockDb = new Mock<localEntities>();
this.mockMembershipService = new Mock<IMembershipService>();
this.mockRoleService = new Mock<IRoleService>();
this.mockUserAgreementRelation = new Mock<DbSet<UserAgreementRelation>>();
this.testUsers = this.CreateSystemUsers();
this.mockDb.Setup(m => m.User).Returns(mockUserSet.Object);
mockUserAgreementRelation.SetupAllProperties();
var agreementRelations = this.testUsers.Select(u => new UserAgreementRelation() { AgentUserID = u.UserId, IsDeleted = null });
mockUserAgreementRelation.As<IQueryable<UserAgreementRelation>>().Setup(ua => ua.GetEnumerator()).Returns(agreementRelations.GetEnumerator());
this.mockDb.Setup(m => m.UserAgreementRelation).Returns(mockUserAgreementRelation.Object);
}
在我的测试中,我设置了一些用户并调用了 var testActiveUsers = testRepo.GetAllActiveUsers();
调用问题行。
这是我感到困惑的地方。
测试通过此代码:
var agreementRelations = databaseContext.UserAgreementRelation().ToList();
var userAgreementFromDB = agreementRelations
.FirstOrDefault(x => x.AgentUserID == ramsUser.UserId && (x.IsDeleted == null || !(bool)x.IsDeleted));
但此代码失败:
var userAgreementFromDB = databaseContext.UserAgreementRelation
.FirstOrDefault(x => x.AgentUserID == ramsUser.UserId && (x.IsDeleted == null || !(bool)x.IsDeleted));
此外,如果我调试并单步执行代码,我可以快速查看模拟的 UserAgreementRelation once。第一次我将看到我正在测试的每个用户的 UserAgreementRelation。如果我 运行 再次观察,即使代码没有推进,枚举也不会产生任何结果。我猜这与问题有关。但是,由于我不知道为什么会发生这种情况,所以我不能说它是如何导致这个问题的。
请记住,更简单的代码(没有 ToList())是生产代码。它目前运行良好,但我的测试失败了。由于这是一个直接的数据库调用,枚举整个 UserAgreementRelation table 不是一个可行的解决方案。
建议?
我一直用 Where 来做这样的事情,试试这个。
var userAgreementFromDB = databaseContext.UserAgreementRelation
.Where(x => x.AgentUserID == ramsUser.UserId && (x.IsDeleted == null || !(bool)x.IsDeleted)).FirstOrDefault();
我不确定我是否理解了问题,因为你没有详细说明它是如何发生的 "fails"。但也许你应该更改部分:
.Setup(ua => ua.GetEnumerator()).Returns(agreementRelations.GetEnumerator())
进入:
.Setup(ua => ua.GetEnumerator()).Returns(() => agreementRelations.GetEnumerator())
原因是如果你测试的代码调用GetEnumerator()
不止一次,Moq不应该交出旧的 "used" 枚举器实例,它可能已经推进到末尾并且可能已被处置。相反,Moq 应该重新 运行 一个 Func<>
,我的箭头 () => agreementRelations.GetEnumerator()
,以获得一个尚未推进 (MoveNext()
) 或关闭 ([=16=) 的新枚举器]).
提问者告诉我(在下面的评论中)他还需要 Setup
IQueryable
的属性 Provider
and Expression
。