枚举Moq的IDbSet会抛出异常:" Collection被修改;枚举操作可能无法执行。"

时间:2017-05-16 07:43:55

标签: c# unit-testing moq mockdbset

我试图使用Moq框架模拟IDbSet。单元测试应该将新记录(实体)添加到现有的Mocked DbSet集合(SetUp)并返回新集合的计数。

我的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命令将抛出异常:

  

&#39; System.InvalidOperationException:&#39;收集已修改;枚举操作可能无法执行。&#39;   &#39;

以下是更改后的单元测试的代码:

[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 个答案:

答案 0 :(得分:2)

迭代器没有重置,因为所有调用都使用了相同的实例,因为设置了moq

.Returns(queryable.GetEnumerator());

每次返回相同的枚举器实例,一旦使用一次将需要重置(导致修改集合后出现异常)。

如果你想在每次调用时都有一个新的枚举器,那么你需要传递Returns一个lambda表达式:

GetEnumerator方法的设置更新为

.Returns(() => queryable.GetEnumerator()); //<-- note the function

每次调用GetEnumerator()时都会调用lambda。所以现在多次枚举模拟应该按预期工作。

这将允许在枚举器中进行多次传递,因为初始设置将为每次调用返回相同的枚举器。由于先前未重置枚举器,因此当您再次尝试遍历它时,您遇到了错误。