使用 DbSet 和 IQueryable 以及 NSubstitute 操作对象会返回错误

2024-01-08

我想用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)阻止并让catchblock 什么都不做,那么测试就不会抛出异常(当然)并且确实会按预期通过。

我知道这不是解决方案,但是我如何实现单元测试的目标DbSet<T>.Add() and DbSet<T>.Remove()方法?


这里发生了什么事?

  1. set.Remove(blog);- 这会调用之前配置的 lambda。
  2. data.Remove(entity);- 该项目已从列表中删除。
  3. dbset.SetupData(data, find);- 我们再次调用SetupData,用新列表重新配置Substitute。
  4. SetupData runs...
  5. 在那里,dbSetup.Remove正在被调用,以便重新配置下次调用Remove时发生的情况。

好的,我们这里有问题。dtSetup.Remove(Arg.Do<T....重新配置任何事情,而是adds当您调用“删除”时,替代者的内部事件列表中应发生的行为。因此,我们当前正在运行之前配置的“删除”操作 (1),同时,在堆栈中,我们将一个操作添加到列表 (5)。当堆栈返回并且迭代器查找要调用的下一个操作时,模拟操作的基础列表已更改。迭代器不喜欢改变。

由此得出结论:当替代品的模拟操作之一正在运行时,我们无法修改其所做的事情。如果您考虑一下,没有人阅读您的测试会认为会发生这种情况,因此您根本不应该这样做。

我们该如何解决它?

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);

    Func<IQueryable<TEntity>> getQuery = () => new InMemoryAsyncQueryable<TEntity>(data.AsQueryable());

    ((IQueryable<TEntity>) dbset).Provider.Returns(info => getQuery().Provider);
    ((IQueryable<TEntity>) dbset).Expression.Returns(info => getQuery().Expression);
    ((IQueryable<TEntity>) dbset).ElementType.Returns(info => getQuery().ElementType);
    ((IQueryable<TEntity>) dbset).GetEnumerator().Returns(info => getQuery().GetEnumerator());

#if !NET40
    ((IDbAsyncEnumerable<TEntity>) dbset).GetAsyncEnumerator()
                                            .Returns(info => new InMemoryDbAsyncEnumerator<TEntity>(getQuery().GetEnumerator()));
    ((IQueryable<TEntity>) dbset).Provider.Returns(info => getQuery().Provider);
#endif

    dbset.Remove(Arg.Do<TEntity>(entity => data.Remove(entity)));
    dbset.Add(Arg.Do<TEntity>(entity => data.Add(entity)));

    return dbset;
}
  1. The getQuerylambda 创建一个新查询。它始终使用捕获的列表data.
  2. All .Returns配置调用使用 lambda。在那里,我们创建一个新的查询实例并将我们的调用委托在那里。
  3. Remove and Add只修改我们捕获的列表。我们不必重新配置 Substitute,因为每次调用都会使用 lambda 表达式重新评估查询。

虽然我真的很喜欢 NSubstitute,但我强烈建议您研究一下Effort,实体框架单元测试工具 https://effort.codeplex.com.

你可以像这样使用它:

// DbContext needs additional constructor:
public class MyDbContext : DbContext
{
    public MyDbContext(DbConnection connection) 
        : base(connection, true)
    {
    }
}

// Usage:
DbConnection connection = Effort.DbConnectionFactory.CreateTransient();    
MyDbContext context = new MyDbContext(connection);

在那里,您有一个实际的 DbContext,您可以使用实体框架为您提供的所有内容,包括使用快速内存​​数据库进行迁移。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 DbSet 和 IQueryable 以及 NSubstitute 操作对象会返回错误 的相关文章

  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • 使用实体框架时是否应该使用部分类作为业务层?

    我正在开发一个使用实体框架的项目 使用EF生成的类的部分类作为业务层可以吗 我开始认为这就是 EF 的用途 我尝试使用 DTO 模式 很快意识到我只是创建了一堆映射类 这重复了我的工作 而且还导致更多的维护工作和额外的层 我想使用自我跟踪实
  • 不支持将数据直接绑定到存储查询(DbSet、DbQuery、DbSqlQuery)

    正在编码视觉工作室2012并使用实体模型作为我的数据层 但是 当页面尝试加载时 上面提到的标题 我使用 Linq 语句的下拉控件往往会引发未处理的异常 下面是我的代码 using AdventureWorksEntities dw new
  • 用于登录 .NET 的堆栈跟踪

    我编写了一个 logger exceptionfactory 模块 它使用 System Diagnostics StackTrace 从调用方法及其声明类型中获取属性 但我注意到 如果我在 Visual Studio 之外以发布模式运行代
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • C#中如何移动PictureBox?

    我已经使用此代码来移动图片框pictureBox MouseMove event pictureBox Location new System Drawing Point e Location 但是当我尝试执行时 图片框闪烁并且无法识别确切
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 如何序列化/反序列化自定义数据集

    我有一个 winforms 应用程序 它使用强类型的自定义数据集来保存数据进行处理 它由数据库中的数据填充 我有一个用户控件 它接受任何自定义数据集并在数据网格中显示内容 这用于测试和调试 为了使控件可重用 我将自定义数据集视为普通的 Sy
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • WPF/C# 将自定义对象列表数据绑定到列表框?

    我在将自定义对象列表的数据绑定到ListBox in WPF 这是自定义对象 public class FileItem public string Name get set public string Path get set 这是列表
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 通过指向其基址的指针删除 POD 对象是否安全?

    事实上 我正在考虑那些微不足道的可破坏物体 而不仅仅是POD http en wikipedia org wiki Plain old data structure 我不确定 POD 是否可以有基类 当我读到这个解释时is triviall
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • 如何防止用户控件表单在 C# 中处理键盘输入(箭头键)

    我的用户控件包含其他可以选择的控件 我想实现使用箭头键导航子控件的方法 问题是家长控制拦截箭头键并使用它来滚动其视图什么是我想避免的事情 我想自己解决控制内容的导航问题 我如何控制由箭头键引起的标准行为 提前致谢 MTH 这通常是通过重写
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐

  • ViewScoped Bean 内 SessionScope 的 ManagedProperty - 瞬态?

    我有这样的 JSF Beans 结构 ManagedBean ViewScoped public class ViewBeany implements Serializable ManagedProperty value sessionBe
  • C# 类型转换:存在显式强制转换但引发转换错误?

    我了解到HashSet实施IEnumerable界面 因此 可以隐式地强制转换HashSet对象进入IEnumerable HashSet
  • Flutter - 拖动标记并获得新位置

    在 Flutter 中 我有一张地图 可以在以下帮助下获取设备位置location https pub dartlang org packages location 然后我使用这个位置在 Google 地图中显示标记谷歌地图颤振 https
  • 在 JSON-LD 中,是否可以为属性值定义 URI 映射?

    假设我们有以下 JSON context name http schema org name status http schema org status name Manu Sporny status trollin The trollin
  • 有没有 Runtime.getRuntime().exec() 的替代方法

    只是想知道 是否有比这更好 更新 更安全 更快等的东西Runtime getRuntime exec 我想在 Linux 上从我的应用程序运行另一个进程 这是我知道的唯一方法 如果有替代方案就好了 怎么样流程构建器 http downloa
  • Sublime Text 3 中的自定义语法

    我正在努力找出如何使用新的 Sublime Text 3 创建新的语法突出显示 sublime 语法风格定义 之前的大多数答案都与旧的做法有关 从 Sublime Text Build 3084 开始 添加了新的语法定义格式 扩展名为 su
  • ASP.NET 主题未正确呈现

    我有一个使用主题的小型网络应用程序 主题在主机上工作 因此在预初始化时 如果主机 a 则加载 x 主题 如果主机 b 则加载 y 主题 在我的代码中 这看起来像 如果 request url host contains a 那么 页面 主题
  • ios 8.4.1 webview黑屏

    我需要在 ios 中在一个简单的 webview 中创建一个应用程序 我用example https github com vandadnp iOS 8 Swift Programming Cookbook blob master chap
  • 在日志传送的辅助服务器上创建用户

    我有一个生产服务器说ServerA我已设置日志传送到ServerB其处于只读模式 此日志传送的目的是降低生产服务器上某些昂贵的查询 痛苦的报告 的负载 现在 如果我必须使用我们的域帐户创建一些登录名 我无法执行此操作 因为辅助数据库位于st
  • 如何从 Rust 中的 Vec 中提取两个可变元素[重复]

    这个问题在这里已经有答案了 我试图从 Vec 中提取两个元素 它始终包含至少两个元素 这两个元素需要可变地提取 因为我需要能够在单个操作中更改这两个元素的值 示例代码 struct Piece x u32 y u32 name static
  • 使用 CSS 将按钮放置在另一个按钮之上

    我在这里需要一些高级 CSS 帮助 我有一个登录按钮和一个注册按钮 我只想一次显示一个 如果用户未登录 注册按钮应出现在登录按钮的顶部 我们有一个复杂而疯狂的后端 如果服务器认为用户未登录 它将生成注册按钮的代码 但是两者都会由服务器输出
  • Windows Python:使用区域设置模块更改编码

    使用Python 2 7 我正在编写一个抽象的网络抓取工具 在显示 打印 某些字符时遇到问题 我收到回溯错误 UnicodeEncodeError ascii codec can t encode character u u2606 in
  • 什么是无符号字符?

    在 C C 中 什么是unsigned char是用来 和普通的有什么不同char 在C 中 有以下三种distinct字符类型 char signed char unsigned char 1 char 如果您使用字符类型text 使用不
  • 如何从组件模板将数组作为 Input() 传递?

    我需要使用绑定将值数组传递给组件 例如 Component selector my component template div div export class MyComponent Input data any 然而 Angular
  • Prolog 中的算术,使用 2 的幂表示数字

    我有两个数字 让我们命名它们N and K 我想写N using K2 的幂 例如如果N 9 and K 4 then N可能N 1 2 2 4 2 0 2 1 2 1 2 2 我的程序应该输出类似的内容N 1 2 2 4 我习惯了C 我在
  • WPF 应用程序的单元测试失败,并出现 NotSupportedException“无法识别 Uri 前缀”

    我目前正在编写单元测试 在这个位置测试失败 并出现 NotSupportedException 无法识别 URI 前缀 经过小型研究 我已经注册了 pack Uri 方案 但它没有帮助 return WaitImageThumbnail W
  • 如何将工作负载项与已提交的更改链接起来?

    我正在使用 Git for Visual Studio Online 我添加了一个产品待办事项列表项 我添加了一些文件并提交更改 由于某种原因没有链接 我对该项目做了更多的提交并同步 我的第一个更改未与待办日志项链接 它仍然在任务下显示为待
  • 如何获取 grep 命令的输出(Python)

    我有一个输入文件 test txt 为 host dc2000 host 192 168 178 2 我想通过使用以下方式获取这些机器的所有地址 grep host root test txt 依此类推 我通过python获得命令输出 im
  • 从字符串Python中获取列表[重复]

    这个问题在这里已经有答案了 例如 我有一个字符串 1 2 3 我怎样才能让她翻一张清单 1 2 3 引号字符串 如果有其他字符 则保留字符串 我怎样才能做到这一点 例子 input output 1 2 3 1 2 3 input outp
  • 使用 DbSet 和 IQueryable 以及 NSubstitute 操作对象会返回错误

    我想用N替补 http nsubstitute github io 通过模拟对 Entity Framework 6 x 进行单元测试DbSet http msdn microsoft com en us library gg696460