关于 c#:Mocking DbContext with NSubstitute and Repository is empty or NotImplementedException

Mocking DbContext with NSubstitute and Repository is empty or NotImplementedException

我正在尝试模拟我的 DbContext 类以使用 NSubstitute 进行单元测试,但我无法通过存储库类从我的假 dbcontext 中恢复这些数据。

我有一个通用类 (SubstituteDbContext) 来创建 sobe DbSet,并将它们设置为我的 DbContext 中的相应 DbSet 属性。

我有下面要模拟的 DbContext 中每个属性的代码:

1
2
3
var data = DbSetData.MyData();
var dbSet = SubstituteDbContext.DbSet(data);
dbContext.MyObject.Returns(dbSet);

我使用以下方法为每个属性创建了一个 DbSet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static DbSet< T > getDbSet< T >(IEnumerable< T > data = null)
    where T : class
{
    var dbSet = Substitute.For<DbSet< T >, IQueryable< T >>();

    if (data != null)
    {
        var queryable = data.AsQueryable();
        ((IQueryable< T >)dbSet).Provider.Returns(queryable.Provider);
        ((IQueryable< T >)dbSet).Expression.Returns(queryable.Expression);
        ((IQueryable< T >)dbSet).ElementType.Returns(queryable.ElementType);
        ((IQueryable< T >)dbSet).GetEnumerator().Returns(queryable.GetEnumerator());
        ((IQueryable< T >)dbSet).AsNoTracking().Returns(queryable);
    }
    return dbSet;
}

在我的 BaseRepository 类(每个存储库的抽象父类)中,我有一些像下面这样的方法来获取数据:

1
2
3
4
5
6
public virtual IEnumerable< T > List(Expression<Func<T, bool>> predicate)
{
    return DbContext.Set< T >()
        .Where(predicate)
        .AsEnumerable();
}

但是当我的调试器运行 BaseRepository 的 List 方法时,我有两种可能的结果(因为我尝试了上面代码的一些变体):

1
2
1o I've got System.NotImplementedException
2o The result returned is empty (but

您的 SUT 调用了 DbContext.Set<T>(),但是您只设置了 dbContext.MyObject.Returns(dbSet)。您需要设置前者。


就像@rgvlee 说我没有正确设置 DbSet。

我已经为 setDbSet<T> 更改了我的 getDbSet<T> 泛型方法,因为我不再返回模拟的 DbSet 并使用 SetValue(来自反射)在我的另一个泛型方法中设置它 im DbContext。

现在我正在 setDbSet<T> 方法中直接从我模拟的 DbContext 设置通用的 Set<T> 属性,不需要直接使用反射设置它。

在新的 setDbSet<T>()(即 getDbSet<T>())的代码下方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void setDbSet< T >(DbContext dbContext, IEnumerable< T > data = null)
    where T : class
{
    var dbSet = Substitute.For<DbSet< T >, IQueryable< T >>();

    if (data != null)
    {
        var queryable = data.AsQueryable();
        ((IQueryable< T >)dbSet).Provider.Returns(queryable.Provider);
        ((IQueryable< T >)dbSet).Expression.Returns(queryable.Expression);
        ((IQueryable< T >)dbSet).ElementType.Returns(queryable.ElementType);
        ((IQueryable< T >)dbSet).GetEnumerator().Returns(queryable.GetEnumerator());
        ((IQueryable< T >)dbSet).AsNoTracking().Returns(queryable);
    }

    dbContext.Set< T >().Returns(dbSet);
}

并且在我调用 setDbSet<T>() 的通用方法中,我删除了下面的行,在该行中,我在 DbContext.

中设置了从 getDbSet<T>() 返回的模拟 dbSet

propertyInfo.SetValue(dbContext, dbSet);