在运行时加载相关组件时是否可以运行单元测试?

时间:2013-01-08 00:28:58

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

是否可以在依赖项上运行单元测试而不使用“copy local”,在运行时加载程序集?

更多详情:

我的.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()

4 个答案:

答案 0 :(得分:2)

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

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

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

使用DeploymentAttribute确实有帮助,但不应该这样做。

答案 3 :(得分:0)

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

关于这个问题:

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

好吧,答案是可能会使用很多依赖关系。

很难像第三方那样遵循所有被动依赖。

我的意思是-如果您测试的程序集依赖于程序集A,则A可能依赖于程序集B。 copy local true将仅复制A,但由于缺少B而在运行时将失败。 考虑像整个组件这样的情况,它们嵌套在以下文件夹中:

  • AppRoot
    • AppHost.exe
    • BuisnessLogic(文件夹)
    • 接口(文件夹)
    • UI(文件夹)
    • 基础设施(文件夹)
    • 3rdParty(文件夹)

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

mstest“ .runsettings”可以在单元测试期间配置类似的程序集探测。 XUnit无法做到这一点。

一种解决方法是开始引用大量的测试组件,发现它们不能被重新使用-但只有在运行测试时才能找到它。

这是一个糟糕而令人沮丧的解决方案。

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

那将非常有帮助。

(实际上是阻止我们从mstest升级到所需XUnit的原因。)