我有一个基于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.实例已设置为
或频率较低的
无法侦听前缀
http://localhost:60666/
,因为它与机器上的现有注册冲突
当这种情况发生时,整个测试套件将在几分钟内无法运行——可能是在某个底层网络绑定超时或被处理时?-几分钟后,一切又开始工作了。
我该如何更优雅地处理这个问题?有没有什么方法可以在初始化AppHost之前强制处理/注销http://localhost:60666/
端点,这样它就会在启动新的服务主机之前杀死任何现有的服务主机?当一个"正确"的失败测试导致1000多个测试失败时,由于其他测试无法初始化其HTTP侦听器,因此越来越难弄清楚发生了什么。
卸载AppDomain:
干净地重新启动ServiceStack主机的唯一方法是卸载它正在运行的应用程序域,并在新的应用程序领域中启动新实例。
你的问题与这个问题有关&答复
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:运行测试:
由于测试运行程序AppDomain
和AppHost
AppDomain
现在不同,我们无法直接从测试中访问AppHost
。因此,我们将测试通过到我们的IsolatedAppHost
的RunTest
方法实例。
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"));
});
}
完整的源代码在这里。我希望这能有所帮助。