DeploymentItem在单独的程序集中中断EntityFramework单元测试

时间:2014-03-28 13:47:49

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

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

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

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

C.Test中,我进行了一次空单元测试DeploymentItem

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

当我分别运行两个测试时,它们都通过了。但是,当我"全部运行"两个测试一起作为同一测试运行的一部分,TestB失败,但有以下异常:

  

"实体框架提供程序类型' System.Data.Entity.SqlServer.SqlProviderServices,   EntityFramework.SqlServer'在具有不变名称&System; System.SData.SqlClient&无法加载。确保使用了程序集限定名称,并且程序集可供正在运行的应用程序使用。有关详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=260882。"

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

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

现在两个测试都通过了TestB创建上下文而不抛出异常。不知何故,在程序集DeploymentItemAttribute中添加C.Test会破坏在单独的程序集DeploymentItem中不使用B.Test的测试(我在{{1}中进行了其他测试使用DeploymentItem,因此删除这个实例并不会从库中删除引用)。它给了我很多时间甚至缩小了目前为止的失败,我完全不知道接下来要做什么才能解决这个问题。

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

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

    C.Test

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

  3. 所以看起来我们有一个答案"如何使我的单元测试工作?"但不是问题"为什么会发生这种情况?"我怀疑,鉴于这个解决方案,EF6在动态加载类型和装配时会快速松动,并且在从某些类型的位置执行测试时,某些装配不会被加载。

3 个答案:

答案 0 :(得分:8)

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

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

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

答案 1 :(得分:1)

其原因来自EF及其提供商之间的松散耦合以及MSTest运行测试的方式。

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

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

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

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

答案 2 :(得分:0)

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

但是,在VS2017的本地计算机上,似乎运行良好。

最终,我能够在本地计算机上复制故障:分别运行测试项目,或者在两者上运行“运行所选项目”都可以。但是,如果我选择非SQL项目(一个具有DeploymentItem的项目),则其他两个将因OP的错误而失败。

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

看来,一旦您解决了VS重新启动的问题,就会发现拥有DeploymentItem导致程序集被放入TestResults目录的“ Out”目录中,而这正是EntityFramework.SqlServer.dll最终从中丢失的地方大概是测试运行程序从哪里运行的。

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

我发现的解决方案:

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

  2. “ var确保DLLIsCopied = System.Data.Entity.SqlServer.SqlProviderServices.Instance;”在共享数据库类的构造函数中。

在其他类似的线程中建议使用

No.2-但让我惊讶的是DeploymentItem对测试运行程序的影响,以及您一起运行 的测试项目的数量>,并且需要手动重建测试项目.....找到解决方案比找出问题要容易得多!