【问题标题】:How do I get around "HttpListenerBase.Instance has already been set" in my ServiceStack-hosted tests?如何在我的 ServiceStack 托管测试中绕过“HttpListenerBase.Instance 已设置”?
【发布时间】:2014-09-25 13:39:15
【问题描述】:

我有一个基于 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/ 端点,所以它会在启动新的服务主机之前杀死任何现有的服务主机?当一个“正确”的失败测试导致 1,000 多次测试失败时,要弄清楚发生了什么变得越来越困难,因为其他测试无法初始化它们的 HTTP 侦听器。

【问题讨论】:

  • 哦。是的,是的,我们做到了——但是将您的解决方案集成到我们的代码库中花了很长时间,以至于我完全忘记了它的来源——抱歉!
  • 太棒了 :) 很高兴它很有用。

标签: servicestack httplistener servicestack-bsd


【解决方案1】:

卸载 AppDomain:

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

您的问题是related to this question & answer

1:创建您的 AppHost(正常):

AppHost 的示例向 IoC 容器注册了一个名为 MyTest 的对象。

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 方法需要在新的AppDomain 中创建IsolatedAppHost 的实例。而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。所以我们将测试传递给IsolatedAppHostRunTest 方法实例。

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

例如:

[Test]
public void TestWillPass()
{
    IsolatedAppHost.RunTest(appHost => {
        var t = appHost.TryResolve<MyTest>();
        Assert.That(t.Name, Is.EqualTo("Hello"));
    });
}

[Test]
public void TestWillFail()
{
    IsolatedAppHost.RunTest(appHost => {
        var t = appHost.TryResolve<MyTest>();
        Assert.That(t.Name, Is.EqualTo("World"));
    });
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    相关资源
    最近更新 更多