I'm using Topshelf to host a Windows Service written in C# and I now want to write some integration tests. My initialisation code is held in a launcher class like the following:
public class Launcher
{
private Host host;
/// <summary>
/// Configure and launch the windows service
/// </summary>
public void Launch()
{
//Setup log4net from config file
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(DEFAULT_CONFIG));
//Setup Ninject dependency injection
IKernel kernel = new StandardKernel(new MyModule());
this.host = HostFactory.New(x =>
{
x.SetServiceName("MyService");
x.SetDisplayName("MyService");
x.SetDescription("MyService");
x.RunAsLocalSystem();
x.StartAutomatically();
x.Service<MyWinService>(s =>
{
s.ConstructUsing(() => kernel.Get<MyWinService>());
s.WhenStarted(w => w.Start());
s.WhenStopped(w => w.Stop());
});
});
this.host.Run(); //code blocks here
}
/// <summary>
/// Dispose the service host
/// </summary>
public void Dispose()
{
if (this.host != null && this.host is IDisposable)
{
(this.host as IDisposable).Dispose();
this.host = null;
}
}
}
I want to write some integration tests to make sure that log4net and Ninject get set up properly and Topshelf launches my service. The problem is, once you call Run()
on the Topshelf host, the code just blocks so my test code never gets run.
I thought of calling Launch()
in a separate thread in the SetUp
section of my tests but then I need a bit of a hack to put in a Thread.Sleep(1000)
to make sure the tests don't run before Launch()
has finished. I can't use a proper sync on it (like a ManualResetEvent
) because the Launch()
never returns. The current code is:
private Launcher launcher;
private Thread launchThread;
[TestFixtureSetUp]
public void SetUp()
{
launcher = new Launcher();
launchThread = new Thread(o => launcher.Launch());
launchThread.Start();
Thread.Sleep(2500); //yuck!!
}
[TestFixtureTearDown]
public void TearDown()
{
if (launcher != null)
{
launcher.Dispose(); //ouch
}
}
Ideally what I'm looking for is a non-blocking way of launching the service and a programmatic way of stopping it again to put in my TearDown
. At the moment my TearDown
just disposes the launcher (so the TearDown
literally tears it down!).
Does anyone have experience in testing Topshelf services in this way? I can do the above relatively easily using the standard ServiceHost
but I much prefer the explicit configuration and ease of installation in Topshelf.