2009-11-08 3 views
1

Я тестирую некоторый код, который работает всякий раз, когда сборки загружаются в appdomain. Для модульного тестирования (в встроенном тестовом хосте VS2k8 в) Я раскручивается новый, однозначно названием AppDomain перед каждым испытанием с идеей, что он должен быть «чистым»:Ассембли таинственно загружены в новые AppDomains

[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 уже имеет сборки, загруженные из всех предыдущих тестов. Как ни странно, два теста получают приложения с разными именами. Тестовые сборки, которые определяют MissingRegistration и InvalidFactories (две разные сборки), никогда не загружаются в приложение apponain по умолчанию для модульного теста. Может ли кто-нибудь объяснить это поведение?

+0

Обновление: Это как-то связано с appSetup.ApplicationBase. Использование другого пути для каждого прогона позволит избежать нежелательных побочных эффектов. – Eric

+0

Update2: Проблема не повторяется, если одни и те же методы запускаются в том же порядке в консольном приложении. Возможно, тестовый узел VS вызывает некоторые проблемы. – Eric

ответ

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] говорит, что он вызывается один раз за тест, но, как говорят документы, тестовая среда Visual Studio вызывает это только один раз для каждого класса, когда он запускает несколько тестов. Я использую другую структуру, поэтому я не уверен в этом.

Update

Теперь, когда я могу видеть остальную часть вашего кода, я вижу, что вы уже принимаете разумные меры предосторожности, чтобы не загружать сборки в родительском AppDomain. Вы говорите, что это действительно не происходит, и я верю вам. Если еще хуже, вы можете попробовать, чтобы ваш SupportingClass вызывал другую сборку, которая затем выполняет ваш тест, но я не думаю, что это что-то изменит.

У меня есть другая теория для вас:

Я где-то читал (на блоге, я думаю), что JITed методы кэшируются и повторно между AppDomains на основе подписи, которая включает некоторые правила сборки нагрузки. Я предполагаю, что это будет ApplicationBase.

Итак, моя теория заключается в том, что когда NET Framework загружает вашу сборку (или, может быть, когда она загружает ваш SupportingClass), она сканирует уже методы JITed, которые она может использовать. Когда он находит ранее JITed-метод, он «разрешает» его (из-за отсутствия лучшего слова) в этом AppDomain, от которого зависит загрузка сборки.

Это объясняет, почему изменение ApplicationBase заставляет его работать: методы JITed не будут повторно использоваться из кеша, и поскольку метод никогда не вызывается, он снова не JITed, поэтому зависимая сборка никогда не загружается.

Мне кажется, что у вас есть хорошее обходное решение при изменении ApplicationBase, или вы можете попробовать изменить данные, если ApplicationBase имеет важное значение для сохранения того же. Я бы подумал, что это будет иметь тот же эффект.

+0

Хорошая идея, но нет любви. Я обновил свое оригинальное сообщение, чтобы отразить результаты этого эксперимента. Я также добавил правильное форматирование (я никогда не писал ранее). – Eric