在运行时加载依赖程序集时能否运行单元测试?

3
可以在不使用“复制本地”依赖项的情况下运行单元测试吗?在运行时加载程序集?
更多细节:
我的.net解决方案看起来像这样:
- MainProject(exe)[依赖于'Class1Project'并在运行时使用'AssemblyLoaderProject'加载它]。 'Class1Project'已设置为“复制本地= false”,而'AssemblyLoaderProject'已设置为“复制本地= 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);
    }
}

当我尝试使用VS2012测试运行器在MSTest上调试时,出现了以下错误提示:

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个回答

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

首先,我建议将Loader.LoadAssemblies移动到标记有TestInitialize属性的方法中,在每个测试之前运行。但是,根据文件在测试执行之前被复制到何处,这也可能行不通。您可能需要在测试设置中启用部署,并使用DeploymentItem属性。

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

根据Loader.LoadAssemblies的确切作用,您可能会遇到其他由程序集加载到多个或不同的加载上下文引起的问题。这可能会导致问题,例如InvalidCastException,其中错误消息说您无法将类型“X”转换为类型“X”。但是,您可能是安全的,因为如果通过探测未找到程序集,则将调用AppDomain.AssemblyResolve将程序集加载到加载上下文中。


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

1
我同意应该有一种方法来配置解析目录,就像Mstest使用“.runsettings”文件一样。
关于这个问题:
更大的问题是:如果您正在进行单元测试,为什么不能将“复制本地”设置为依赖项的真实值,以便它们与测试程序集一起复制?
好吧,答案是很多依赖项的依赖项可能会被使用。
很难跟踪所有被动依赖项,比如第三方依赖项。
我的意思是 - 如果您的测试程序集依赖于程序集A,那么A可能依赖于B。 复制本地true只会复制A,但在运行时会因为B缺失而失败。 考虑像整个程序集嵌套在文件夹中的情况,例如:
- AppRoot - AppHost.exe - BuisnessLogic(文件夹) - Interfaces(文件夹) - UI(文件夹) - Infrastracture(文件夹) - 3rdParty(文件夹)
并且apphost具有App.config,该文件允许目录探测以使运行时解析所有嵌套目录。

mstest中的“.runsettings”可以在单元测试期间配置类似的程序集探测,而XUnit则无法实现。

一种解决方法是引用很多程序集到你的测试中,在每次发现无法解析时添加一个程序集,但这只有在运行测试时才能发现。

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

我建议提出更改请求,让XUnit也支持目录探测配置。

那将非常有帮助。

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


0

VS2012测试运行器存在一个错误。在我的项目中,我已将所有依赖项设置为复制本地=true,但仍有一些dll未能复制到“Out”文件夹,导致测试失败。如果我在VS2010下运行相同的测试,一切都运行得很完美。

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


0

你需要确保DLL的'Class1Project'对加载程序可用。如果你不想将项目设置为总是复制,可以使用DeploymenItem属性来复制进行测试,这样加载程序就能找到它。


加载器使用Assembly.LoadFrom(FULL_PATH_TO_Class1ProjectDll),因此它应该在任何地方都可以使用,无需复制文件。 - avivr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接