这些单例单元测试是否实际按预期工作

本文关键字:工作 单例 单元测试 是否 | 更新日期: 2023-09-27 18:30:56

我有一个引导程序对象,我正在尝试测试(使用 xunit)。 测试似乎通过了,但我在我使用的一个测试运行器(ncrunch)中看到了一些奇怪的东西。 我同时使用ncrunch和resharper xunit流道。 我的想法是获取单一实例所在的程序集,将其加载到新的应用程序域中,使用反射运行我的测试,然后卸载应用程序域。 正如我所说,测试在 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 在处理其他应用程序域时可能存在错误,但更有可能的是我实际上并不了解应用程序域发生了什么,但我不确定从哪里继续。

另外,我确实知道单例是不好的,但我相信引导程序是它们实际上有用的一个地方。 您希望保证它们仅引导一次。

这些单例单元测试是否实际按预期工作

除非我在这里遗漏了一些东西......否则看起来您实际上并没有在其他应用程序域中调用任何内容。您的反射发生在当前应用程序域中。看看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;
    }
}