我想用N替补 http://nsubstitute.github.io/通过模拟对 Entity Framework 6.x 进行单元测试DbSet http://msdn.microsoft.com/en-us/library/gg696460(v=vs.113).aspx。幸运的是,Scott Xu https://github.com/scott-xu提供了良好的单元测试库,EntityFramework.测试.起订量 https://www.nuget.org/packages/EntityFrameworkTesting.Moq/ using Moq http://www.moqthis.com/。所以,我修改了他的代码以适合 NSubstitute,到目前为止看起来不错,直到我想测试DbSet<T>.Add()
, DbSet<T>.Remove()
方法。这是我的代码位:
public static class NSubstituteDbSetExtensions
{
public static DbSet<TEntity> SetupData<TEntity>(this DbSet<TEntity> dbset, ICollection<TEntity> data = null, Func<object[], TEntity> find = null) where TEntity : class
{
data = data ?? new List<TEntity>();
find = find ?? (o => null);
var query = new InMemoryAsyncQueryable<TEntity>(data.AsQueryable());
((IQueryable<TEntity>)dbset).Provider.Returns(query.Provider);
((IQueryable<TEntity>)dbset).Expression.Returns(query.Expression);
((IQueryable<TEntity>)dbset).ElementType.Returns(query.ElementType);
((IQueryable<TEntity>)dbset).GetEnumerator().Returns(query.GetEnumerator());
#if !NET40
((IDbAsyncEnumerable<TEntity>)dbset).GetAsyncEnumerator().Returns(new InMemoryDbAsyncEnumerator<TEntity>(query.GetEnumerator()));
((IQueryable<TEntity>)dbset).Provider.Returns(query.Provider);
#endif
...
dbset.Remove(Arg.Do<TEntity>(entity =>
{
data.Remove(entity);
dbset.SetupData(data, find);
}));
...
dbset.Add(Arg.Do<TEntity>(entity =>
{
data.Add(entity);
dbset.SetupData(data, find);
});
...
return dbset;
}
}
我创建了一个测试方法,例如:
[TestClass]
public class ManipulationTests
{
[TestMethod]
public void Can_remove_set()
{
var blog = new Blog();
var data = new List<Blog> { blog };
var set = Substitute.For<DbSet<Blog>, IQueryable<Blog>, IDbAsyncEnumerable<Blog>>()
.SetupData(data);
set.Remove(blog);
var result = set.ToList();
Assert.AreEqual(0, result.Count);
}
}
public class Blog
{
...
}
当测试方法调用时出现问题set.Remove(blog)
。它抛出一个InvalidOperationException
错误消息为
集合已修改;枚举操作可能无法执行。
这是因为假货data
当对象被修改时set.Remove(blog)
方法被调用。然而,最初的斯科特使用的方式Moq
不会导致问题。
因此,我包裹了set.Remove(blog)
方法与try ... catch (InvalidOperationException ex)
阻止并让catch
block 什么都不做,那么测试就不会抛出异常(当然)并且确实会按预期通过。
我知道这不是解决方案,但是我如何实现单元测试的目标DbSet<T>.Add()
and DbSet<T>.Remove()
方法?