我如何解决" HttpListenerBase.Instance已被设置"在我的ServiceStack托管测试中?

时间:2014-09-25 13:39:15

标签: servicestack httplistener servicestack-bsd

我已经在ServiceStack 3.9上构建了一个项目,并且使用ServiceStack的自托管API工具进行了广泛的测试覆盖。

我的很多测试装置看起来像这样:

private const string URL_BASE = "http://localhost:60666/";

[TestFixtureSetUp]
public void TestFixtureSetUp() {
    AppHost = new HttpTestableAppHost(FakeServices.Register);
    AppHost.Init();
    AppHost.Start(URL_BASE);
}

[TestFixtureTearDown]
public void TestFixtureTearDown() {
    AppHost.Dispose();
    AppHost = null;
}

问题在于,如果测试失败,TearDown似乎总是干净利落地运行 - 然后项目上的每个其他测试都会失败并出现相同的错误

  

System.IO.InvalidDataException:已设置HttpListenerBase.Instance

或不太常见

  

无法侦听前缀http://localhost:60666/,因为它与计算机上的现有注册冲突

当发生这种情况时,整个测试套件将在几分钟内无法运行 - 可能是因为某些基础网络绑定超时或被处置? - 几分钟后,它们全部重新开始工作。

如何更优雅地处理这个问题?有没有什么方法可以在初始化我的AppHost之前强行处理/取消注册http://localhost:60666/端点,所以在开始新的主机之前它会杀死任何现有的服务主机?当一个人适当的时候,弄清楚正在发生什么事情变得越来越困难。测试失败导致1000多次测试失败,因为其他测试无法初始化他们的HTTP侦听器。

1 个答案:

答案 0 :(得分:3)

卸载AppDomain:

干净地重新启动ServiceStack主机的唯一方法是卸载它运行的应用程序域并在新的应用程序域中启动一个新实例。

您的问题是related to this question & answer

1:创建AppHost(按照正常情况):

示例AppHost,它将名为MyTest的对象注册到IoC容器。

public class AppHost : AppSelfHostBase
{
    public AppHost(): base("My ServiceStack Service", typeof(AppHost).Assembly)
    {
    }

    public override void Configure(Funq.Container container)
    {
        container.Register<MyTest>(c => new MyTest());
    }
}

public class MyTest
{
    public string Name { get { return "Hello"; } }
}

2:创建IsolatedAppHost类:

IsolatedAppHost类用于启动应用程序主机,该主机将在隔离的AppDomain中运行。您可以在此处启动和配置所需的AppHost,例如HttpTestableAppHost

public class IsolatedAppHost : MarshalByRefObject
{
    readonly AppHost Host;

    public IsolatedAppHost()
    {
        // Start your HttpTestableAppHost here
        Host = new AppHost();
        Host.Init();
        Host.Start("http://*:8090/");
        Console.WriteLine("ServiceStack is running in AppDomain '{0}'", AppDomain.CurrentDomain.FriendlyName);
    }

    public void RunTest(Action<AppHost> test)
    {
        test.Invoke(Host);
    }

    public void Teardown()
    {
        if(Host != null)
        {
            Console.WriteLine("Shutting down ServiceStack host");
            if(Host.HasStarted)
                Host.Stop();
            Host.Dispose();
        }
    }
}

3:创建TestFixture

您的TestFixureSetup方法需要在新IsolatedAppHost中创建AppDomain的实例。 TestFixtureTearDown将确保AppHost和域名正确关闭。

[TestFixture]
public class Test
{
    AppDomain ServiceStackAppDomain;
    IsolatedAppHost IsolatedAppHost;

    [TestFixtureSetUp]
    public void TestFixtureSetup()
    {
        // Get the assembly of our host
        var assemblyName = typeof(IsolatedAppHost).Assembly.GetName();

        // Create new AppDomain
        ServiceStackAppDomain = AppDomain.CreateDomain("ServiceStackAppDomain");

        // Load our assembly
        ServiceStackAppDomain.Load(assemblyName);

        // Create instance
        var handle = ServiceStackAppDomain.CreateInstance(assemblyName.FullName, "MyApp.Tests.IsolatedAppHost");

        // Unwrap so we can access methods
        IsolatedAppHost = (IsolatedAppHost)handle.Unwrap();
    }

    [TestFixtureTearDown]
    public void TestFixtureTearDown()
    {
        // Tell ServiceStack to stop the host
        IsolatedAppHost.Teardown();

        // Shutdown the ServiceStack application
        AppDomain.Unload(ServiceStackAppDomain);
        ServiceStackAppDomain = null;
    }

    // Tests go here
}

4:运行测试:

由于测试运动员AppDomainAppHost AppDomain现在不同,我们无法直接从我们的测试中访问AppHost。因此,我们将测试传递给RunTest的{​​{1}}方法实例。

IsolatedAppHost

例如:

IsolatedAppHost.RunTest(appHost => {
    // Your test goes here
});

full source code is here。我希望有所帮助。