我正在测试一些代码,这些代码在程序集加载到appdomain时都能正常工作。对于单元测试(在VS2k8的内置测试主机中),我在每次测试之前启动一个新的,具有唯一名称的appdomain,并认为它应该是“干净的”:
[TestInitialize()]
public void CalledBeforeEachTestMethod()
{
AppDomainSetup appSetup = new AppDomainSetup();
appSetup.ApplicationBase = @"G:\<ProjectDir>\bin\Debug";
Evidence baseEvidence = AppDomain.CurrentDomain.Evidence;
Evidence evidence = new Evidence( baseEvidence );
_testAppDomain = AppDomain.CreateDomain( "myAppDomain" + _appDomainCounter++, evidence, appSetup );
}
[TestMethod]
public void MissingFactoryCausesAppDomainUnload()
{
SupportingClass supportClassObj = (SupportingClass)_testAppDomain.CreateInstanceAndUnwrap(
GetType().Assembly.GetName().Name,
typeof( SupportingClass ).FullName );
try
{
supportClassObj.LoadMissingRegistrationAssembly();
Assert.Fail( "Should have nuked the app domain" );
}
catch( AppDomainUnloadedException ) { }
}
[TestMethod]
public void InvalidFactoryMethodCausesAppDomainUnload()
{
SupportingClass supportClassObj = (SupportingClass)_testAppDomain.CreateInstanceAndUnwrap(
GetType().Assembly.GetName().Name,
typeof( SupportingClass ).FullName );
try
{
supportClassObj.LoadInvalidFactoriesAssembly();
Assert.Fail( "Should have nuked the app domain" );
}
catch( AppDomainUnloadedException ) { }
}
public class SupportingClass : MarshalByRefObject
{
public void LoadMissingRegistrationAssembly()
{
MissingRegistration.Main();
}
public void LoadInvalidFactoriesAssembly()
{
InvalidFactories.Main();
}
}
如果每个测试都单独运行,我发现它可以正常工作; appdomain已创建,只加载了少量预期的程序集。但是,如果连续运行多个测试,则每个_testAppDomain都已从所有先前的测试中加载了程序集。奇怪的是,这两个测试得到了具有不同名称的appdomains。定义MissingRegistration和InvalidFactories(两个不同的程序集)的测试程序集永远不会加载到单元测试的默认appdomain中。任何人都可以解释这种行为吗?
答案 0 :(得分:0)
听起来正如所发生的那样,程序集正在父AppDomain中加载。如果是这样,您的错误就在于测试代码中其他地方使用_testAppDomain的详细信息,现在就是如何创建它。
理想情况下,测试工具代码应该在AppDomain中运行,然后在AppDomain中运行的方法应该对测试中的组件进行实际加载。
以下是一个示例,说明如何防止父AppDomain加载测试程序集:
void TestRunner()
{
testProxy =
(TestProxy)_testAppDomain.CreateInstanceAndUnwrap(
typeof(TestProxy).Assembly.FullName,
typeof(TestProxy).FullName)
testProxy.RunTest(testAssembly, typeName);
}
public class TestProxy : MarshalByRefObject
{
public void Runtest(string testAssembly, string typeName)
{
var testType = Assembly.Load(testAssembly).GetType(typeName);
// run tests in testType using reflection or whatever
}
}
但是,在您的特定情况下,您可能本身并没有任何测试程序集,因此可能不适用。
我还注意到你的[TestInitialize]评论说每次测试都会调用一次,但是IIRC,文档说Visual Studio的测试框架在运行多个测试时每个类只调用一次。我使用不同的框架,所以我不确定。
<强>更新强>
现在我可以看到你的其余代码了,我可以看到你已经采取了合理的预防措施,不在父AppDomain中加载程序集。你说那确实没有发生,我相信你。如果情况变得更糟,你可以尝试让你的SupportingClass调用另一个程序集然后进行测试,但我真的不认为这会改变任何东西。
我确实有另一种理论:
我在某个地方(我认为在博客上)读到JITed方法在AppDomains之间基于包含一些程序集加载规则的签名进行缓存和重用。我假设这将包括ApplicationBase。
所以我的理论是,当.NET Framework加载你的程序集时(或者当它加载你的SupportingClass时),它正在扫描它可以使用的已经JITed的方法。当它找到以前的JITed方法时,它会在该AppDomain中“启用”它(因为没有更好的单词),这会触发它所依赖的程序集加载。
这可以解释为什么changin ApplicationBase使它工作:JITed方法不会从缓存中重用,并且由于该方法永远不会被调用,因此它不再被JIT,因此从不加载依赖程序集。
在我看来,您在改变ApplicationBase时有一个很好的解决方法,或者如果ApplicationBase保持不变,您可以尝试更改证据。我认为这会产生同样的效果。