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.
It means that integration testing starts when code writing is finished and the first version of a product is ready for the release. It can be time-saving since a team doesn't pause the development to check every unit.
Integration tests verify that different modules or services used by your application work well together. For example, it can be testing the interaction with the database or making sure that microservices work together as expected.
Unlike integration testing, E2E testing immediately checks how the entire system works. It allows testers to check the application subsystems and layers from the UI right to the database. Besides, there are two basic approaches to run an E2E test: vertical and horizontal. Vertical E2E is complex technically.
https://github.com/Topshelf/Topshelf/blob/v2.3/src/Topshelf/Config/Builders/RunBuilder.cs#L113 I think is what you want. AfterStartingService
could be used to set a ManualResetEvent
from a different thread.
Now this is might work out for you, but this feels like overly complicated and could be validated just by deploying to dev/staging and doing smoke tests to your system. However, without more understanding of your environment, that might not be possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With