Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/284.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# NSubstitute DbSet/IQueryable<;T>;_C#_Entity Framework_Dbcontext_Nsubstitute_Dbset - Fatal编程技术网

C# NSubstitute DbSet/IQueryable<;T>;

C# NSubstitute DbSet/IQueryable<;T>;,c#,entity-framework,dbcontext,nsubstitute,dbset,C#,Entity Framework,Dbcontext,Nsubstitute,Dbset,因此,EntityFramework6的可测试性比以前的版本要好得多。互联网上也有类似Moq的框架,但事实是,我更喜欢使用NSubstitute。我已经将“非查询”示例翻译为使用NSubstitute,但我无法理解“查询测试” Moq的items.As().Setup(m=>m.Provider)如何返回(data.Provider)翻译为NSubstitute?我认为类似于((IQueryable)items.Provider.Returns(data.Provider)但这不起作用。我还尝试

因此,EntityFramework6的可测试性比以前的版本要好得多。互联网上也有类似Moq的框架,但事实是,我更喜欢使用NSubstitute。我已经将“非查询”示例翻译为使用NSubstitute,但我无法理解“查询测试”

Moq的
items.As().Setup(m=>m.Provider)如何返回(data.Provider)翻译为NSubstitute?我认为类似于
((IQueryable)items.Provider.Returns(data.Provider)但这不起作用。我还尝试了
items.AsQueryable().Provider.Returns(data.Provider)但这也不起作用

我得到的解释是:

“System.NotImplementedException:成员'IQueryable.Provider' 尚未在继承自的类型“DbSet
1Proxy”上实现
'DbSet
1'。'DbSet'1'的测试双精度必须提供 所使用的方法和属性。”

让我引用上面链接中的代码示例。此代码示例使用Moq模拟DbContext和DbSet

public void GetAllBlogs_orders_by_name()
{
  // Arrange
  var data = new List<Blog>
  {
     new Blog { Name = "BBB" },
     new Blog { Name = "ZZZ" },
     new Blog { Name = "AAA" },
  }.AsQueryable();

  var mockSet = new Mock<DbSet<Blog>>();
  mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
  mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
  mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
  mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

  var mockContext = new Mock<BloggingContext>();
  mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);

  // ...
}
public void GetAllBlogs\u orders\u by\u name()
{
//安排
var数据=新列表
{
新博客{Name=“BBB”},
新博客{Name=“ZZZ”},
新博客{Name=“AAA”},
}.AsQueryable();
var mockSet=new Mock();
mockSet.As().Setup(m=>m.Provider).返回(data.Provider);
mockSet.As().Setup(m=>m.Expression).Returns(data.Expression);
mockSet.As().Setup(m=>m.ElementType).Returns(data.ElementType);
mockSet.As().Setup(m=>m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext=new Mock();
Setup(c=>c.Blogs).Returns(mockSet.Object);
// ...
}
这就是我和NSubstitute一起走过的路

public void GetAllBlogs_orders_by_name()
{
  // Arrange
  var data = new List<Blog>
  {
     new Blog { Name = "BBB" },
     new Blog { Name = "ZZZ" },
     new Blog { Name = "AAA" },
  }.AsQueryable();

  var mockSet = Substitute.For<DbSet<Blog>>();
  // it's the next four lines I don't get to work
  ((IQueryable<Blog>) mockSet).Provider.Returns(data.Provider);
  ((IQueryable<Blog>) mockSet).Expression.Returns(data.Expression);
  ((IQueryable<Blog>) mockSet).ElementType.Returns(data.ElementType);
  ((IQueryable<Blog>) mockSet).GetEnumerator().Returns(data.GetEnumerator());

  var mockContext = Substitute.For<BloggingContext>();
  mockContext.Blogs.Returns(mockSet);

  // ...
}
public void GetAllBlogs\u orders\u by\u name()
{
//安排
var数据=新列表
{
新博客{Name=“BBB”},
新博客{Name=“ZZZ”},
新博客{Name=“AAA”},
}.AsQueryable();
var mockSet=Substitute.For();
//接下来的四行我不去上班了
((IQueryable)mockSet.Provider.Returns(data.Provider);
((IQueryable)mockSet.Expression.Returns(data.Expression);
((IQueryable)mockSet.ElementType.Returns(data.ElementType);
((IQueryable)mockSet.GetEnumerator()。返回(data.GetEnumerator());
var mockContext=Substitute.For();
mockContext.Blogs.Returns(mockSet);
// ...
}

所以问题是,;如何替换IQueryable的属性(如提供者)?

您不需要模拟IQueryable的所有部分。当我使用NSubstitute模拟EF DbContext时,我会这样做:

interface IContext
{
  IDbSet<Foo> Foos { get; set; }
}

var context = Substitute.For<IContext>();

context.Foos.Returns(new MockDbSet<Foo>());
接口IContext
{
IDbSet Foos{get;set;}
}
var context=Substitute.For();
返回(新的MockDbSet());
通过一个简单的IDbSet实现,为我的MockDbSet()创建一个列表或其他东西


一般来说,您应该模拟接口,而不是类型,因为NSubstitute只会覆盖虚拟方法。

多亏了Kevin,我在代码翻译中发现了问题

正在模拟DbSet,但NSubstitute需要接口实现。因此,NSubstitute的Moqs
new Mock()
等效于
Substitute.for()
。您并不总是需要提供接口,因此我感到困惑。但在这一具体案例中,它变得至关重要

结果还表明,在使用接口IDbSet时,我们不必强制转换为Queryable

因此,工作测试代码:

public void GetAllBlogs_orders_by_name()
{
  // Arrange
  var data = new List<Blog>
  {
    new Blog { Name = "BBB" },
    new Blog { Name = "ZZZ" },
    new Blog { Name = "AAA" },
  }.AsQueryable();

  var mockSet = Substitute.For<IDbSet<Blog>>();
  mockSet.Provider.Returns(data.Provider);
  mockSet.Expression.Returns(data.Expression);
  mockSet.ElementType.Returns(data.ElementType);
  mockSet.GetEnumerator().Returns(data.GetEnumerator());

  var mockContext = Substitute.For<BloggingContext>();
  mockContext.Blogs.Returns(mockSet);

  // Act and Assert ...
}
public void GetAllBlogs\u orders\u by\u name()
{
//安排
var数据=新列表
{
新博客{Name=“BBB”},
新博客{Name=“ZZZ”},
新博客{Name=“AAA”},
}.AsQueryable();
var mockSet=Substitute.For();
mockSet.Provider.Returns(data.Provider);
mockSet.Expression.Returns(data.Expression);
返回(data.ElementType);
mockSet.GetEnumerator().Returns(data.GetEnumerator());
var mockContext=Substitute.For();
mockContext.Blogs.Returns(mockSet);
//行动和主张。。。
}
我已经编写了一个小的扩展方法来清理单元测试的排列部分

public static class ExtentionMethods
{
    public static IDbSet<T> Initialize<T>(this IDbSet<T> dbSet, IQueryable<T> data) where T : class
    {
        dbSet.Provider.Returns(data.Provider);
        dbSet.Expression.Returns(data.Expression);
        dbSet.ElementType.Returns(data.ElementType);
        dbSet.GetEnumerator().Returns(data.GetEnumerator());
        return dbSet;
    }
}

// usage like:
var mockSet = Substitute.For<IDbSet<Blog>>().Initialize(data);
公共静态类扩展方法
{
公共静态IDbSet初始化(此IDbSet dbSet,IQueryable数据),其中T:class
{
返回(data.Provider);
返回(data.Expression);
返回(data.ElementType);
返回(data.GetEnumerator());
返回dbSet;
}
}
//用法如下:
var mockSet=Substitute.For().Initialize(数据);
这不是问题,但如果您还需要能够支持异步操作:

public static IDbSet<T> Initialize<T>(this IDbSet<T> dbSet, IQueryable<T> data) where T : class
{
  dbSet.Provider.Returns(data.Provider);
  dbSet.Expression.Returns(data.Expression);
  dbSet.ElementType.Returns(data.ElementType);
  dbSet.GetEnumerator().Returns(data.GetEnumerator());

  if (dbSet is IDbAsyncEnumerable)
  {
    ((IDbAsyncEnumerable<T>) dbSet).GetAsyncEnumerator()
      .Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));
    dbSet.Provider.Returns(new TestDbAsyncQueryProvider<T>(data.Provider));
  }

  return dbSet;
}

// create substitution with async
var mockSet = Substitute.For<IDbSet<Blog>, IDbAsyncEnumerable<Blog>>().Initialize(data);
// create substitution without async
var mockSet = Substitute.For<IDbSet<Blog>>().Initialize(data);
公共静态IDbSet初始化(此IDbSet dbSet,IQueryable数据),其中T:class
{
返回(data.Provider);
返回(data.Expression);
返回(data.ElementType);
返回(data.GetEnumerator());
if(dbSet是可枚举的)
{
((IDbAsyncEnumerable)dbSet.GetAsyncEnumerator()
.Returns(新的TestDbAsyncEnumerator(data.GetEnumerator());
返回(新的TestDbAsyncQueryProvider(data.Provider));
}
返回dbSet;
}
//使用异步创建替换
var mockSet=Substitute.For().Initialize(数据);
//创建无异步的替换
var mockSet=Substitute.For().Initialize(数据);

发生这种情况是因为NSSubstitute语法特定。例如:

((IQueryable<Blog>) mockSet).Provider.Returns(data.Provider);
((IQueryable)mockSet.Provider.Returns(data.Provider);
NSubstitute调用提供程序的getter,然后指定返回值。这个getter调用没有被替代者截获,您会得到一个异常。发生这种情况的原因是DbQuery类中IQueryable.Provider属性的显式实现

您可以使用NSub显式地为多个接口创建替换,并创建覆盖所有指定接口的代理。然后调用接口
// Create a substitute for DbSet and IQueryable types:
var mockSet = Substitute.For<DbSet<Blog>, IQueryable<Blog>>();
    
// And then as you do:
((IQueryable<Blog>) mockSet).Provider.Returns(data.Provider);
((IQueryable<Blog>) mockSet).Expression.Returns(data.Expression);
((IQueryable<Blog>) mockSet).ElementType.Returns(data.ElementType);
((IQueryable<Blog>) mockSet).GetEnumerator().Returns(data.GetEnumerator());
 public static class CustomTestUtils
{
    public static DbSet<T> FakeDbSet<T>(List<T> data) where T : class
    {
        var _data = data.AsQueryable();
        var fakeDbSet = Substitute.For<DbSet<T>, IQueryable<T>>();
        ((IQueryable<T>)fakeDbSet).Provider.Returns(_data.Provider);
        ((IQueryable<T>)fakeDbSet).Expression.Returns(_data.Expression);
        ((IQueryable<T>)fakeDbSet).ElementType.Returns(_data.ElementType);
        ((IQueryable<T>)fakeDbSet).GetEnumerator().Returns(_data.GetEnumerator());

        fakeDbSet.AsNoTracking().Returns(fakeDbSet);

        return fakeDbSet;
    }

}
public void GetAllBlogs_orders_by_name()
{
  // Arrange
  var data = new List<Blog>
  {
     new Blog { Name = "BBB" },
     new Blog { Name = "ZZZ" },
     new Blog { Name = "AAA" },
  };

  var mockContext = Substitute.For<BloggingContext>();

  // Create and assign the substituted DbSet
  var mockSet = data.GenerateMockDbSet();
  mockContext.Blogs.Returns(mockSet);

  // act
}
public async Task GetAllBlogs_orders_by_name()
{
  // Arrange
  var data = new List<Blog>
  {
     new Blog { Name = "BBB" },
     new Blog { Name = "ZZZ" },
     new Blog { Name = "AAA" },
  };

  var mockContext = Substitute.For<BloggingContext>();

  // Create and assign the substituted DbSet
  var mockSet = data.GenerateMockDbSetForAsync(); // only change is the ForAsync version of the method
  mockContext.Blogs.Returns(mockSet);

  // act
}