5

Używam usługi Topshelf do obsługi usługi systemu Windows napisanej w języku C#, a teraz chcę napisać kilka testów integracji. Mój kod inicjalizacji odbywa się w klasie wyrzutni jak następuje:Testy integracyjne na górnej półce, aby uruchomić usługę 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; 
     } 
    } 
} 

chcę napisać kilka testów integracyjnych, aby upewnić się, że log4net i Ninject się prawidłowo skonfigurowany i Topshelf uruchamia moją usługę. Problem polega na tym, że po wywołaniu Run() na hoście Topshelf, kod blokuje się, więc mój kod testowy nigdy nie zostanie uruchomiony.

Myślałem o wywołanie Launch() w osobnym wątku w dziale SetUp moich testów, ale potem muszę trochę hack umieścić w Thread.Sleep(1000) aby upewnić się, że testy nie są uruchamiane przed Launch() zakończył. Nie mogę użyć właściwej synchronizacji (jak ManualResetEvent), ponieważ Launch() nigdy nie wróci. Bieżący kod jest:

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

Idealnie co szukam jest non-blocking sposobem uruchomienia usługi i programowy sposób zatrzymując go ponownie, aby umieścić w moim TearDown. W tej chwili mój TearDown po prostu uruchamia program uruchamiający (więc TearDown dosłownie rozrywa go!).

Czy ktoś ma doświadczenie w testowaniu usług Topshelf w ten sposób? Mogę to zrobić stosunkowo łatwo przy użyciu standardu ServiceHost, ale wolę jawną konfigurację i łatwość instalacji w Topshelf.

Odpowiedz

2

https://github.com/Topshelf/Topshelf/blob/v2.3/src/Topshelf/Config/Builders/RunBuilder.cs#L113 Myślę, że jest to, co chcesz. AfterStartingService może być użyty do ustawienia ManualResetEvent z innego wątku.

Teraz może się to udać, ale wydaje się, że jest to zbyt skomplikowane i można je zweryfikować po wdrożeniu do dev/inscenizacji i wykonaniu testów dymu w systemie. Jednak bez większego zrozumienia twojego środowiska może to nie być możliwe.

+3

link jest uszkodzony – SteveC

+0

@ SteveC już nie. Link jest dostępny w tej chwili. – dotnetguy

0

Miałem ten sam problem dzisiaj i wybrałem izolowanie usługi instanciation i interakcji od rzeczywistego hostingu Topshelf (który składa się wyłącznie z rozwiązania twojej usługi przy użyciu Ninject w twoim przypadku).

Adam Rodger ma rację bytu i po krótkim spojrzeniu na metodę Run dla ConsoleRunHost, to zawiesi oczekiwanie na ManualResetEvent i nie da ci kontroli, dopóki usługa nie zostanie zakończona.

w miejscu, do kodu testy dym/regresji, po prostu jądra i moduł do swojego sposobu SetUp, rozwiązać usługę, wykonać testy i wyrzucać go w TearDown

Powiązane problemy