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