1

我有一个基于 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/,因为它与机器上的现有注册冲突

发生这种情况时,整个测试套件会在几分钟内无法运行 - 大概是在某些底层网络绑定超时或被处置时?- 然后几分钟后它又开始工作了。

我怎样才能更优雅地处理这个问题?有什么方法可以http://localhost:60666/在初始化我的 AppHost 之前强制处置/取消注册该端点,所以它会在启动新的服务主机之前杀死任何现有的服务主机?当一个“正确”的失败测试导致 1,000 多个测试失败时,要弄清楚发生了什么变得越来越困难,因为其他测试无法初始化它们的 HTTP 侦听器。

4

1 回答 1

3

卸载 AppDomain:

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

您的问题与此问题和答案有关

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

注册使用 IoC 容器AppHost调用的对象的示例。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方法将需要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直接从我们的测试中访问。所以我们将测试传递RunTestIsolatedAppHost.

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"));
    });
}

完整的源代码在这里。我希望这会有所帮助。

于 2014-09-25T14:32:40.320 回答