【问题标题】:DeploymentItem breaks EntityFramework unit test in separate assemblyDeploymentItem 在单独的程序集中破坏了 EntityFramework 单元测试
【发布时间】:2014-05-08 00:33:09
【问题描述】:

我的解决方案中有 5 个程序集:ABB.TestCC.TestBC 都引用 A(并且不相互引用)。 B.Test 引用 ABC.Test 引用 AC

B.Test 中,我正在创建一个在B 中定义的EntityFramework6 DbContext 对象:

[TestMethod]
public void TestB() {
    MyBContext c = new MyBContext();
}

C.Test 中,我有一个带有DeploymentItem 的空单元测试:

[TestMethod]
[DeploymentItem("data.txt")]
public void TestC() { }

当我分别运行这两个测试时,它们都通过了。但是,当我将两个测试作为同一测试运行的一部分“运行所有”时,TestB 失败并出现以下异常:

"实体框架提供程序类型'System.Data.Entity.SqlServer.SqlProviderServices, 无法加载在 ADO.NET 提供程序的应用程序配置文件中注册的具有不变名称“System.Data.SqlClient”的 EntityFramework.SqlServer'。确保使用程序集限定名称并且程序集可用于正在运行的应用程序。请参阅http://go.microsoft.com/fwlink/?LinkId=260882 了解更多信息。”

当我更改TestC 以注释掉DeploymentItem 属性时,如下所示:

[TestMethod]
//[DeploymentItem("data.txt")]
public void TestC() { }

两个测试现在都通过了TestB 创建上下文而不抛出异常。不知何故,在程序集C.Test 中添加DeploymentItemAttribute 正在破坏一个在单独的程序集B.Test 中不使用DeploymentItem 的测试(而且我在C.Test 中有其他使用DeploymentItem 的测试,所以删除这个实例不会从库中删除引用)。我花了很多时间才把故障范围缩小到这一步,我完全不知道下一步该怎么做才能解决这个问题。

编辑:我在 MSDN 上找到了一些似乎可以解决此问题的信息(尽管我不明白为什么)。

  1. 通过 Resharper 测试运行器或其他测试运行器运行单元测试似乎可以解决问题。只有当我通过 VisualStudio(2012,如果重要)运行单元测试时,测试才会失败
  2. 将以下代码添加到定义 DbContext 的程序集中似乎可以解决问题:

    static MyDbContext () {
        var _ = typeof(System.Data.Entity.SqlServer.SqlProviderServices);
    }
    

    (MSDN 上的答案建议将其添加到上下文类本身,我将其添加到该上下文的工厂类型中,而不是具有相同的有益结果)。

所以看起来我们对“如何让我的单元测试工作?”有了一个答案。但不是“为什么首先会发生这种情况?”的问题。我怀疑,考虑到这个解决方案,EF6 在动态加载类型和程序集方面有点快和松散,当从某些类型的地方执行测试时,某些程序集没有被加载。

【问题讨论】:

    标签: c# .net entity-framework unit-testing


    【解决方案1】:

    我刚刚花了几个小时来解决同样的问题。我有 3 个测试项目,其中两个使用与 EF6/SQL 相关的程序集。当我将 DeploymentItem 添加到第三个时,我的 TeamCity 构建开始使两个 SQL 测试项目失败。

    然而,在我的 VS2017 本地机器上,似乎运行良好。

    最终我能够在我的本地机器上复制失败:单独运行测试项目,或者在两者上运行选择是可以的。但是,如果我选择了非 SQL 项目(带有 DeploymentItem 的项目),其他两个项目将因 OP 错误而失败。

    还要注意,VS 的行为并不是完全可重复的:我从 #3 中注释掉了 DeploymentItem,重新运行测试并选择了所有 3 个 - 仍然失败。重新启动VS,重新选择并重新运行-一切正常!

    看来,一旦您解决了 VS 重启问题,部署项会导致程序集被放入 TestResults 目录中的“Out”目录中,而这正是 EntityFramework.SqlServer.dll 最终丢失的地方并且,大概是测试运行器运行的地方。

    首先添加 DeploymentItem 也很痛苦 - 再次,它需要重新启动 VS(或至少手动重建测试项目)才能“注意到”需要拉入文件。整个概念似乎有点古怪......

    我找到的解决方案:

    1. 任何测试类中的 Geo 属性

    2. "var ensureDLLIsCopied = System.Data.Entity.SqlServer.SqlProviderServices.Instance;"在共享数据库类的构造函数中。

    第 2 项在其他类似线程中被建议 - 但让我感到震惊的是 DeploymentItem 对 测试运行器 的影响,以及您一起运行的测试项目的数量 >,并且需要手动重建测试项目......找到解决方案比找出复制问题容易得多!

    【讨论】:

      【解决方案2】:

      问题是因为编译器没有输出EntityFramework.SqlServer.dll,因为它没有检测到它是否在某个地方使用(它只通过依赖注入使用)。最简单的解决方案是在测试中使用其中一种程序集。

      例如您可以创建一个属性或方法(您不需要使用它,只需将其公开就足够了)。为了解决这个问题,我在测试助手中创建了一个属性:

      public static System.Data.Entity.SqlServer.SqlProviderServices EnsureAssemblySqlServerIsCopied { get; set; }
      

      【讨论】:

        【解决方案3】:

        造成这种情况的原因在于 EF 与其提供程序之间的松散耦合以及 MSTest 运行测试的方式。

        关于提供者,实体框架可以与各种不同的提供者一起工作。 SQL Server 提供程序只是一个示例。 EntityFramework.dll 对 SQL Server 提供程序程序集没有硬依赖,因为在将 EF 与任何其他提供程序一起使用时不需要它。

        这意味着在使用 EF 时,您应该确定将使用哪些提供程序,并确保提供程序程序集与您的应用程序或测试一起部署。这通常通过将提供程序 NuGet 包安装到应用程序或测试项目中来完成。如果 NuGet 包安装到应用程序/测试项目中,则提供程序程序集将被放置到 bin 文件夹中,无论它是否在代码中直接引用。如果提供者 NuGet 包仅安装到解决方案中的某个其他项目(例如类库)而不是直接安装到应用程序/测试项目中,则情况并非如此。

        由于历史原因,SQL Server 提供程序包含在主 Entity Framework NuGet 包中,因此对于 SQL Server 提供程序,需要在应用程序/测试项目中安装 EntityFramework 包。如果您不想将提供程序/EntityFramework 包安装到应用程序/测试项目中,那么您可以设置一些其他逻辑来确保部署程序集。

        现在有了 MSTest,事情变得更加棘手,因为它有自己的部署规则和配置。这意味着仅安装 NuGet 包可能还不够,您可能需要设置特定的 MSTest 部署规则以确保正确复制程序集。另一种解决方案是不使用 MSTest。在 EF 团队中,我们几年前从 MSTest 切换到 xUnit,并且自从这样做以来,此类问题的数量要少得多。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-28
          • 1970-01-01
          • 1970-01-01
          • 2023-01-31
          • 2011-08-04
          • 1970-01-01
          相关资源
          最近更新 更多