Действительно ли эти одноэлементные модульные тесты работают так, как ожидалось?

У меня есть объект начальной загрузки, который я пытаюсь протестировать (используя 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 имеет ошибку при работе с другими доменами приложений, но более вероятно, что я на самом деле не понимаю, что происходит с доменами приложений, но я не уверен, с чего продолжать дальше.

Кроме того, я знаю, что синглтоны — это плохо, но я считаю, что загрузчики — это то место, где они действительно полезны. Вы хотите гарантировать, что они загружаются только один раз.


person Josh    schedule 11.09.2013    source источник


Ответы (1)


Если я что-то здесь не упустил... не похоже, что вы на самом деле что-то вызываете в своем другом домене приложения. Ваше отражение происходит в текущем домене приложения. Взгляните на метод DoCallback: http://msdn.microsoft.com/en-us/library/system.appdomain.docallback.aspx

public class Tests
{
    [Fact]
    public void TryingToAccessBootedKernelBeforeBootThrowsException()
    {
        var appDomain = AppDomain.Create(Guid.NewGuid());
        try
        {
            appDomain.DoCallBack(new CrossAppDomainDelegate(TryingToAccessBootedKernelBeforeBootThrowsException_AppDomainCallback));
        }
        catch (Exception e)
        {
            Assert.IsType(typeof(InvalidOperationException), e.InnerException);
        }

        AppDomain.Unload(appDomain);
    }

    public static void TryingToAccessBootedKernelBeforeBootThrowsException_AppDomainCallback()
    {
        var bootstrapper = BootStrapper.Booted;
    }
}
person Tallek    schedule 25.09.2013
comment
Хорошо, да, ты прав. Это намного сложнее, чем я думал. Мне придется немного переосмыслить это. - person Josh; 27.09.2013