5

J'utilise Topshelf pour héberger un service Windows écrit en C# et je veux maintenant écrire des tests d'intégration. Mon code d'initialisation est maintenu dans une classe de lanceur comme ce qui suit:Tests d'intégration avec Topshelf pour démarrer un service Windows C#

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

Je veux écrire des tests d'intégration pour vous assurer que log4net et Ninject se mettre en place correctement et Topshelf lance mon service. Le problème est, une fois que vous appelez Run() sur l'hôte Topshelf, le code bloque juste de sorte que mon code de test ne soit jamais exécuté.

Je pensais que d'appeler Launch() dans un thread séparé dans la section SetUp de mes tests, mais je besoin d'un peu un hack pour mettre dans un Thread.Sleep(1000) pour vous assurer que les tests ne fonctionnent pas avant Launch() a terminé. Je ne peux pas utiliser une synchronisation correcte dessus (comme un ManualResetEvent) parce que le Launch() ne revient jamais. Le code actuel est:

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 
    } 
} 

Idéalement ce que je suis à la recherche est un moyen non-blocage du lancement du service et d'une manière programmatique de l'arrêter à nouveau pour mettre dans mon TearDown. En ce moment, mon TearDown ne dispose que du lanceur (donc le TearDown le déchire littéralement!).

Est-ce que quelqu'un a de l'expérience dans le test des services Topshelf de cette façon? Je peux faire ce qui précède relativement facilement en utilisant la norme ServiceHost mais je préfère de loin la configuration explicite et la facilité d'installation dans Topshelf.

Répondre

2

https://github.com/Topshelf/Topshelf/blob/v2.3/src/Topshelf/Config/Builders/RunBuilder.cs#L113 Je pense que c'est ce que vous voulez. AfterStartingService peut être utilisé pour définir un ManualResetEvent à partir d'un thread différent. Maintenant, cela pourrait fonctionner pour vous, mais cela semble trop compliqué et pourrait être validé simplement en déployant à dev/staging et en faisant des tests de fumée sur votre système. Cependant, sans plus de compréhension de votre environnement, cela pourrait ne pas être possible.

+3

Le lien est rompu – SteveC

+0

@ SteveC plus maintenant. Le lien est accessible pour le moment. – dotnetguy

0

J'ai eu le même problème aujourd'hui et j'ai choisi d'isoler l'instanciation de service et l'interaction de l'hébergement réel de Topshelf (qui consiste simplement à résoudre votre service en utilisant Ninject dans votre cas). Adam Rodger a un bon point et après avoir jeté un coup d'oeil à la méthode Run pour le ConsoleRunHost, il se bloquera en attendant un ManualResetEvent et ne vous rendra le contrôle jusqu'à ce que le service se termine.

A votre place, au code de vos tests de fumée/régression, juste obtenir le noyau et le module dans votre méthode SetUp, résoudre le service, faire vos tests et jetez-le dans le TearDown

Questions connexes