У меня есть объект начальной загрузки, который я пытаюсь протестировать (используя xunit). Кажется, что тесты проходят, но я вижу некоторые странные вещи в одном из тестовых бегунов, которые я использую (ncrunch). Я использую как ncrunch, так и решарпер xunit runner. Моя идея заключалась в том, чтобы взять сборку, в которой находится синглтон, загрузить ее в новый домен приложения, запустить мои тесты с использованием отражения, а затем выгрузить домен приложения. Как я уже сказал, тесты проходят как в ncrunch, так и в resharper, но ncrunch не показывает пути выполнения, которые я ожидаю. Вот код:
public class Bootstrapper
{
private static Bootstrapper booted;
public Bootstrapper()
{
// performs boot tasks
}
public static void Boot()
{
if (booted == null)
{
var staticboot = new Bootstrapper();
Booted = staticboot;
}
}
public static Bootstrapper Booted
{
get
{
if (booted == null) throw new InvalidOperationException("Should call Boot() before accessing the booted object");
return booted;
}
set { booted = value; }
}
}
public class Tests
{
[Fact]
public void TryingToAccessBootedKernelBeforeBootThrowsException()
{
var setup = this.SetupTestingDomainWithAssembly("StackOverflowQuestion.Tests.dll");
var kernelType = setup.Item2.GetType("StackOverflowQuestion.Tests.Bootstrapper");
var bootedkernelProperty = kernelType.GetProperty("Booted");
try
{
bootedkernelProperty.GetValue(null);
}
catch (Exception e)
{
Assert.IsType(typeof(InvalidOperationException), e.InnerException);
}
AppDomain.Unload(setup.Item1);
}
[Fact]
public void CanAccessKernelAfterBooting()
{
var setup = this.SetupTestingDomainWithAssembly("StackOverflowQuestion.Tests.dll");
var kernelType = setup.Item2.GetType("StackOverflowQuestion.Tests.Bootstrapper");
var bootMethod = kernelType.GetMethod("Boot");
bootMethod.Invoke(null, new object[] { });
var bootedkernelProperty = kernelType.GetProperty("Booted");
Assert.DoesNotThrow(() => bootedkernelProperty.GetValue(null));
AppDomain.Unload(setup.Item1);
}
[Fact]
public void BootIsIdempotent()
{
var setup = this.SetupTestingDomainWithAssembly("StackOverflowQuestion.Tests.dll");
var kernelType = setup.Item2.GetType("StackOverflowQuestion.Tests.Bootstrapper");
var bootMethod = kernelType.GetMethod("Boot");
bootMethod.Invoke(null, new object[] {});
var bootedkernelProperty = kernelType.GetProperty("Booted");
var bootedKernel = (Bootstrapper)bootedkernelProperty.GetValue(null);
bootMethod.Invoke(null, new object[] {});
var secondCall = (Bootstrapper)bootedkernelProperty.GetValue(null);
Assert.Equal(bootedKernel, secondCall);
AppDomain.Unload(setup.Item1);
}
private Tuple<AppDomain, Assembly> SetupTestingDomainWithAssembly(string assemblyPath)
{
// we guarantee that each domain will have a unique name.
AppDomain testingDomain = AppDomain.CreateDomain(DateTime.Now.Ticks.ToString());
var pancakesAssemblyName = new AssemblyName();
pancakesAssemblyName.CodeBase = assemblyPath;
var assembly = testingDomain.Load(pancakesAssemblyName);
return new Tuple<AppDomain, Assembly>(testingDomain, assembly);
}
}
Теперь я понимаю, что есть некоторая очистка, которая должна произойти с кодом, но я был счастлив видеть, что они все зеленые. Если я играю с ними, чтобы заставить их потерпеть неудачу, это работает, как и ожидалось. Единственное, что немного вонюче, это то, что ncrunch сообщает о странных путях выполнения. В частности, ncrunch показывает, что строка, выдающая исключение о недопустимой операции, никогда не выполняется.
Я предполагаю, что возможно, что ncrunch имеет ошибку при работе с другими доменами приложений, но более вероятно, что я на самом деле не понимаю, что происходит с доменами приложений, но я не уверен, с чего продолжать дальше.
Кроме того, я знаю, что синглтоны — это плохо, но я считаю, что загрузчики — это то место, где они действительно полезны. Вы хотите гарантировать, что они загружаются только один раз.