使用Moq模拟的IDbSet枚举时抛出异常:“Collection was modified; enumeration operation may not execute.”

4
我将尝试使用Moq框架模拟IDbSet。这个单元测试应该向已经设置的Mocked DbSet集合中添加一个新的记录(实体),并返回新集合的计数。
我的TestInitialize设置如下:
public class BlogTests
    {
        private IRepository _repository;

        [TestInitialize]
                public void Setup()
                {
                    var blogEntries = new List<BlogEntry>
                    {
                        new BlogEntry()
                        {
                            //...init the object
                        }
                    };
                    var queryableBlogEntries = QueryableDbSetMock.GetQueryableMockDbSet<BlogEntry>(blogEntries);
                    var repoMock = new Mock<IRepository>();
                    repoMock.Setup(x => x.BlogEntries).Returns(queryableBlogEntries);
                    _repository = repoMock.Object;
                }

以下是返回模拟IDbSet的方法,根据给定的List:

public class QueryableDbSetMock
    {
        public static IDbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
        {
            var queryable = sourceList.AsQueryable();

            var dbSet = new Mock<IDbSet<T>>();
            dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
            dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
            dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
            dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
            dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(s => sourceList.Add(s)); 
            return dbSet.Object;
        }
    }

在单元测试中,我会创建一个新的BlogEntry对象并尝试将其添加到模拟的IDbSet中,然后期望集合的总数为两个元素/记录。
以下单元测试成功:
public void IndexTest()
        {
            //Arrange
            var entries = _repository.BlogEntries;

            var newEntry = new BlogEntry()
            {
                //...init the second object
            };

            //Act
            entries.Add(newEntry);
            var count = entries.Count();

            //Assert
            Assert.AreEqual(2, count);
        }

但是当我尝试使用GetEnumerator方法进行计数时,Enumerator.MoveNext命令会抛出异常:'System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

下面是修改后的单元测试代码:

[TestMethod()]
        public void IndexTest2()
        {
            //Arrange
            var entries = _repository.BlogEntries;

            var newEntry = new BlogEntry()
            {
                //...init the second object
            };

            //Act
            entries.Add(newEntry);
            var enumerator = entries.GetEnumerator();
            int count = 0;
            while (enumerator.MoveNext()) //Throws Exception
            {
                count++;
            }
            //Assert
            Assert.AreEqual(2, count);
        }

我的疑惑在于第一个单元测试似乎没有问题地计算了更改的集合数量,但尝试采用较为困难的计数方式时,测试失败了。

如有任何澄清,将不胜感激!

1个回答

4
迭代器没有重置,因为由于moq的设置方式,相同的实例被用于所有调用。
.Returns(queryable.GetEnumerator());

每次返回相同的枚举器实例,使用一次后需要重置(在修改集合后导致异常)。
如果您想在每次调用时获得新的枚举器,则需要传递一个 lambda 表达式给 Returns
更新 GetEnumerator 方法的设置为:
.Returns(() => queryable.GetEnumerator()); //<-- note the function

每次调用 GetEnumerator() 都会调用 lambda。现在多次枚举模拟的操作应该能够按预期工作。
这样可以允许对枚举器进行多次遍历,因为初始设置将为每个调用返回相同的枚举器。由于之前没有重置枚举器,因此尝试再次遍历时会遇到错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接