【问题标题】:Is it possible to run unit tests when loading dependent assemblies at run-time?在运行时加载依赖程序集时是否可以运行单元测试?
【发布时间】:2013-01-08 00:28:58
【问题描述】:

是否可以在没有“复制本地”依赖项的情况下运行单元测试,在运行时加载程序集?

更多细节:

我的 .net 解决方案如下所示:

  • MainProject (exe) [依赖于“Class1Project”并在运行时使用“AssemblyLoaderProject”加载它]。

    'Class1Project' 设置为 'copy local = false' 和 'AssemblyLoaderProject' 设置为 'copy local = true'

  • Class1Project (dll)

  • AssemblyLoaderProject(在运行时使用 AppDomain.CurrentDomain.AssemblyResolve 和 Assembly.LoadFrom 加载和解析依赖项程序集)

  • UnitTestsProject(Nunit 或 MSTest)

在单元测试项目中,我正在尝试测试“Class1Project”,我希望将其配置设置为与 MainProject 相同。

意思是,UnitTestProject 还使用 'copy local = false' 引用 'Class1Project' 和 'copy local = true' 引用 'AssemblyLoaderProject',并使用它在运行时加载程序集。

但由于某种原因,单元测试无法执行,运行程序抛出 FileNotFoundException 指定它无法解析“Class1Project”程序集。

尝试调试它,我发现测试运行程序甚至没有找到告诉 AssemblyLoaderProject 加载程序集的代码。

测试代码如下所示:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        Loader.LoadAssemblies();
        Class1 cls = new Class1();
        Assert.IsTrue(true);
    }
}

这是我尝试在 MSTest 上使用 VS2012 测试运行程序对其进行调试时收到的错误消息:

Test Name:  TestMethod1
Test FullName:  UnitTestProject1.UnitTest1.TestMethod1
Test Source:    c:\Users\user\Documents\Visual Studio 2012\Projects\ClassLibrary1\UnitTestProject1\UnitTest1.cs : line 13
Test Outcome:   Failed
Test Duration:  0:00:00.1177608

Result Message: 
Test method UnitTestProject1.UnitTest1.TestMethod1 threw exception: 
System.IO.FileNotFoundException: Could not load file or assembly 'ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.=== Pre-bind state information ===
LOG: User = \user
LOG: DisplayName = ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/Users/user/Documents/Visual Studio 2012/Projects/ClassLibrary1/UnitTestProject1/bin/Debug
LOG: Initial PrivatePath = NULL
Calling assembly : UnitTestProject1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 11.0\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\TESTWINDOW\vstest.executionengine.x86.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Users/user/Documents/Visual Studio 2012/Projects/ClassLibrary1/UnitTestProject1/bin/Debug/ClassLibrary1.DLL.
LOG: Attempting download of new URL file:///C:/Users/user/Documents/Visual Studio 2012/Projects/ClassLibrary1/UnitTestProject1/bin/Debug/ClassLibrary1/ClassLibrary1.DLL.
LOG: Attempting download of new URL file:///C:/Users/user/Documents/Visual Studio 2012/Projects/ClassLibrary1/UnitTestProject1/bin/Debug/ClassLibrary1.EXE.
LOG: Attempting download of new URL file:///C:/Users/user/Documents/Visual Studio 2012/Projects/ClassLibrary1/UnitTestProject1/bin/Debug/ClassLibrary1/ClassLibrary1.EXE.
Result StackTrace:  at UnitTestProject1.UnitTest1.TestMethod1()

【问题讨论】:

    标签: c# .net unit-testing nunit mstest


    【解决方案1】:

    假设Loader.LoadAssemblies 是注册AssemblyResolve 事件的方法,您将收到此异常,因为运行时无法通过探测找到ClassLibrary1.dll。重要的是要了解为什么探测会在这里发挥作用。调用探测是因为在您可以通过 Loader.LoadAssemblies 加载任何程序集之前,该方法必须是 Jitted。为了正确地进行jit,运行时必须为编译时引用的类型加载程序集。由于您已将“本地复制”设置为 false,因此文件 ClassLibrary1.dll 未被复制且不在探测路径中。因此,您的FileNotFoundException

    首先,我会尝试将Loader.LoadAssemblies 移动到标有TestInitialize 属性的方法中,以便在每次测试之前运行。但是,根据在测试执行之前复制文件的位置,这也可能不起作用。您可能必须在测试设置中启用部署并使用DeploymentItem 属性。

    一个更大的问题是:如果您进行单元测试,为什么不能将依赖项的 'copy local' 设置为 true 以便它们与测试程序集一起复制?

    根据Loader.LoadAssemblies 所做的具体操作,您可能会遇到由于程序集被加载到多个或不同的加载上下文而导致的其他问题。这可能会导致像InvalidCastException 这样的问题,错误表明您不能将类型“X”转换为类型“X”。但是,您可能是安全的,因为如果通过探测未找到程序集,则会调用 AppDomain.AssemblyResolve 将程序集加载到 Load 上下文中。

    【讨论】:

    • 在这种情况下,在 TestInitialize 方法中调用“Loader.LoadAssemblies”有效。加载器使用完整路径来加载class1project,所以我不需要使用复制本地或启用部署。您能否解决我的测试项目包含从 Class1 派生的类(位于 Class1Project 中)的情况?在这种情况下,当我尝试在测试运行器中加载程序集时会发生 jit 和探测阶段,它会失败。
    • 如果我在我的主 .exe 程序集中做同样的事情,创建一个从 Class1 派生的类,应用程序将运行得很好。它是否仅在第一次访问派生类时尝试 JIT(发生在 Lodaer 运行之后),并且在测试上下文中,测试运行程序在文件加载时尝试 JIT 所有程序集的类型?也许是为了解析哪一个包含测试?
    • @avivr 我相信你是完全正确的。测试框架正在加载测试项目中的所有类型,寻找测试。这发生在TestInitialize 之前,所以LoadAssemblies 在探测发生之前没有运行,并且抛出了相同的异常。
    • @avivr 我再问一遍:为什么不能简单地将'copy local'设置为true。您的测试显然是针对 Class1Project 编译的,并将其设置为 false 会导致各种问题。如果您想测试真实的集成场景,请创建 两个 测试项目。一个引用 Class1Project(纯单元测试),一个不引用它,动态加载它(集成/组件测试)。
    【解决方案2】:

    我同意应该有一种方法来配置解析目录,就像 Mstest 使用“.runsettings”文件一样。

    关于问题:

    一个更大的问题是:如果您进行单元测试,为什么不能将依赖项的 'copy local' 设置为 true,以便它们与测试程序集一起复制?

    嗯,答案是可能会用到很多依赖的依赖。

    很难像第 3 方一样遵循所有被动依赖项。

    我的意思是 - 如果您测试的程序集依赖于程序集 A,那么 A 可能依赖于 B。 copy local true 将仅复制 A,但会在运行时失败,因为 B 丢失。 考虑像整个程序集嵌套在以下文件夹中的情况:

    • AppRoot
      • AppHost.exe
      • BusnessLogic(文件夹)
      • 接口(文件夹)
      • 用户界面(文件夹)
      • 基础设施(文件夹)
      • 第 3 方(文件夹)

    并且 apphost 有 App.config,它允许目录探测以使运行时解析所有嵌套目录。

    mstest ".runsettings" 可以在单元测试期间配置类似的程序集探测。 XUnit 做不到。

    一种解决方法是开始引用您的测试的大量程序集,每个程序集在您发现它之后都无法解决 - 但您只有在运行测试时才能找到它。

    这是一个糟糕且令人讨厌的解决方案。

    我投票赞成插入一个更改请求,即 XUnit 也将支持目录探测配置。

    那会很有帮助。

    (这实际上阻碍了我们从 mstest 升级到所需的 XUnit)。

    【讨论】:

      【解决方案3】:

      您必须使 DLL'Class1Project' 可用于加载程序,因此,如果您不想将项目设置为始终复制它,可以使用 DeploymenItem属性复制它进行测试,以便负载可以找到它。

      【讨论】:

      • 加载程序使用Assembly.LoadFrom(FULL_PATH_TO_Class1ProjectDll),所以它应该可以在任何地方使用,而无需复制文件。
      【解决方案4】:

      There is a bug in VS2012 test runner. 在我的项目中,我将所有依赖项都设置为 copy local = true ,但仍有一些 dll 没有被复制到“Out”文件夹,导致测试失败。如果我在 VS2010 下运行相同的测试,一切都会完美运行。

      使用 DeploymentAttribute 确实有帮助,但它不应该是必需的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-28
        • 1970-01-01
        • 2012-08-02
        相关资源
        最近更新 更多