UusingDbSetwithNSubstituteandIQueryableOperationObjectsReturnsErrors我想使用NSubstitute通过模拟DbSet对EntityFramework6.x进行单元测试。幸运的是,ScottXu使用Moq提供了一个很好的单元测试库EntityFramework.Testing.Moq。因此,我修改了他的代码以适应NSubstitute,它一直看起来很好,直到我想测试DbSet.Add()、DbSet.Remove()方法。这是我的代码位:publicstaticclassNSubstituteDbSetExtensions{publicstaticDbSetSetupData(thisDbSetdbset,ICollectiondata=null,Funcfind=null)whereTEntity:class{data=data??新列表();找=找??(o=>空);varquery=newInMemoryAsyncQueryable(data.AsQueryable());((IQueryable)dbset).Provider.Returns(query.Provider);((IQueryable)dbset).Expression.Returns(query.Expression);((IQueryable)dbset).ElementType.Returns(query.ElementType);((IQueryable)dbset).GetEnumerator().Returns(query.GetEnumerator());#if!NET40((IDbAsyncEnumerable)dbset).GetAsyncEnumerator().Returns(newInMemoryDbAsyncEnumerator(query.GetEnumerator()));((IQueryable)dbset).Provider.Returns(query.Provider);#endif...dbset.Remove(Arg.Do(entity=>{data.Remove(entity);dbset.SetupData(data,find);}));...dbset.Add(Arg.Do(entity=>{data.Add(entity);dbset.SetupData(data,find);});...returndbset;}}我创建了一个测试方法,如:[TestClass]公开课ManipulationTests{[TestMethod]publicvoidCan_remove_set(){varblog=newBlog();vardata=newList{博客};varset=Substitute.For().SetupData(数据);设置.Remove(博客);varresult=set.ToList();Assert.AreEqual(0,result.Count);}}publicclassBlog{...}当测试方法调用set.Remove(blog)时出现问题,它抛出InvalidOperationException并显示集合被修改的错误信息;可能无法执行枚举操作。这是因为在调用set.Remove(blog)方法时修改了虚拟数据对象。然而,最初Scott使用Moq的方式并没有导致问题。所以,我用try...catch(InvalidOperationExceptionex)块包装了set.Remove(blog)方法,让catch块什么也不做,然后测试异常不会被抛出(当然)并且确实通过了预期的。我知道这不是解决方案,但如何实现对DbSet.Add()和DbSet.Remove()方法进行单元测试的目标?这里发生了什么?设置。删除(博客);-这会调用之前配置的lambda。数据。删除(实体);–该项目已从列表中删除。dbset.SetupData(数据,查找);-我们再次调用SetupData以使用新列表重新配置Substitute。SetupData运行...在那里,调用dbSetup.Remove以重新配置下次调用Remove时发生的情况。好的,我们这里有问题。dtSetup.Remove(Arg.Do没有重新配置任何东西,而是在Substitute的内部列表中添加了一个行为,这在您调用Remove时发生。所以我们当前正在运行之前配置的Removeaction(1)同时,在堆栈中,我们向列表(5)添加一个动作。当堆栈返回并且迭代器查找要调用的下一个动作时,模拟动作的底层列表已经改变。迭代器不喜欢变化。这导致结论:当我们不能在mockaction运行时修改substitute做了什么。如果你想一想,没有人阅读你的测试会认为这发生了,所以你根本不应该这样做。我们如何解决它?publicstaticDbSetSetupData(这个DbSetdbset,ICollectiondata=null,Funcfind=null)whereTEntity:class{data=data??新列表();找=找??(o=>空);Func>getQuery=()=>newInMemoryAsyncQueryable(data.AsQueryable());((IQueryable)dbset).Provider.Returns(info=>getQuery().Provider);((IQueryable)dbset).Expression.Returns(info=>getQuery().Expression);((IQueryable)dbset).ElementType.Returns(info=>getQuery().ElementType);((IQueryable)dbset).GetEnumerator().Returns(info=>getQuery().GetEnumerator());#if!NET40((IDbAsyncEnumerable)dbset).GetAsyncEnumerator().Returns(info=>newInMemoryDbAsyncEnumerator(getQuery().GetEnumerator()));((IQueryable)dbset).Provider.Returns(info=>getQuery().Provider);#endifdbset.Remove(Arg.Do(entity=>data.Remove(entity)));dbset.Add(Arg.Do(entity=>data.Add(entity)));返回数据库集;}getQuerylambda创建一个新查询。它始终使用捕获的列表数据。所有.Returns配置调用都使用lambda。在那里,我们创建了一个新的查询实例并在那里委托我们的调用。Remove和Add只修改我们捕获的列表。我们不必重新配置替换,因为每次调用都会使用lambda表达式重新评估查询。虽然我非常喜欢NSubstitute,但我强烈建议您查看实体框架单元测试工具Effort。您可以像这样使用它://DbContext需要额外的构造函数:publicclassMyDbContext:DbContext{publicMyDbContext(DbConnectionconnection):base(connection,true){}}//用法:DbConnectionconnection=Effort.DbConnectionFactory.CreateTransient();MyDbContextcontext=newMyDbContext(connection);你有一个实际的DbContext,你可以使用EntityFramework为你提供的一切,包括迁移,以使用快速内存??数据库。以上就是C#学习教程:用NSubstitute使用DbSet和IQueryable操作对象会返回错误分享的所有内容。如果对大家有用,需要了解更多C#学习教程,希望大家多多关注---本文来自网络收藏,不代表立场,如涉及侵权,请右击联系管理员删除。如需转载请注明出处: