Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integration Tests With Topshelf to Start a C# Windows Service

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.

like image 608
Adam Rodger Avatar asked Jul 31 '12 14:07

Adam Rodger


People also ask

At which phase should integration testing begins?

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.

What do you test for integration testing?

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.

Is E2E an integration test?

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.


1 Answers

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.

like image 105
Travis Avatar answered Oct 19 '22 18:10

Travis