UnitTests & Threading – Ist grün wirklich grün?

Ich bin kürzlich auf ein Problem gestoßen: In ReSharper wurden UnitTests grün angezeigt, obwohl diese fehlschlugen. Im konkreten Fall handelt es sich um einen UnitTest im Rahmen von Logik, die parallel abläuft. Tritt in den Threads eine Exception auf, so wird dies nicht als Fehler behandelt. Der UnitTest ist weiterhin grün, wenn der Assert trotzdem korrekt ist.

Beispiel: Stellen wir uns zwei Worker Threads vor, die bei einem Controller nach neuen Druckaufträgen fragen. Der Controller ist als Singleton implementiert (oder idealerweise wird dies schon out of the box vom IoC Container gemacht). Tritt nun eine Exception in den Threads auf, der Assert ist aber trotzdem korrekt, so wird der UnitTest grün angezeigt. Hier der Code dazu:

Implementierung Controller:

   1: class PrintJobMediator : IMediatePrintjobs

   2: {

   3:     //lock object for the method

   4:     private object _lockObject = new object();

   5:  

   6:     //instance of the singleton

   7:     private static volatile PrintJobMediator instance;

   8:  

   9:     //lock object for singleton

  10:     private static object syncRoot = new Object();

  11:  

  12:     //private ctor

  13:     private PrintJobMediator() { }

  14:  

  15:     //returns the singleton instance

  16:     public static PrintJobMediator Instance

  17:     {

  18:         get

  19:         {

  20:             if (instance == null)

  21:             {

  22:                 lock (syncRoot)

  23:                 {

  24:                     if (instance == null)

  25:                         instance = new PrintJobMediator();

  26:                 }

  27:             }

  28:  

  29:             return instance;

  30:         }

  31:     }

  32:  

  33:     int _result;

  34:     public int Next()

  35:     {

  36:         lock (_lockObject)

  37:         {

  38:             System.Threading.Thread.Sleep(1000);

  39:             _result += 1;

  40:  

  41:             if (_result == 1)

  42:                 throw new ApplicationException();

  43:         }

  44:  

  45:         return _result;

  46:     }

  47: }

Unit Test:

   1: [Subject(typeof(PrintJobMediator))]

   2: public class When_2_worker_ask_for_new_jobs

   3: {

   4:     static PrintJobMediator _jobMediator;

   5:     static Thread _thread1;

   6:     static Thread _thread2;

   7:     static TimeSpan _timeTaken;

   8:  

   9:     Establish context = () =>

  10:     {

  11:         _jobMediator = PrintJobMediator.Instance;

  12:         _thread1 = new Thread(() => _jobMediator.Next());

  13:         _thread2 = new Thread(() => _jobMediator.Next());

  14:     };

  15:  

  16:     Because of = () =>

  17:     {

  18:         _timeTaken = Measure.Time(() =>

  19:         {

  20:             _thread1.Start();

  21:             _thread2.Start();

  22:             _thread1.Join();

  23:             _thread2.Join();

  24:         });

  25:     };

  26:  

  27:     It should_run_jobs_sequentially = () => _timeTaken

  28:         .ShouldBeGreaterThanOrEqualTo(TimeSpan.FromSeconds(2));

  29: }

  30:  

  31: public static class Measure

  32: {

  33:     public static TimeSpan Time(Action timedAction)

  34:     {

  35:         var watch = new System.Diagnostics.Stopwatch();

  36:         watch.Start();

  37:  

  38:         timedAction();

  39:  

  40:         watch.Stop();

  41:         return watch.Elapsed;

  42:     }

  43: }

 

In ReSharper wird der Test als erfolgreich angezeigt:

image

Wenn man sich die Details anschaut, sieht man folgendes:

image

In der Implementierung schmeiße ich im ersten Thread eine Exception (Zeile 42). Da trotzdem die Gesamtdauer der Ausführung 2 Sekunden ist, ist der Assert korrekt. Der UnitTest bricht nicht ab, weil seit .NET 2.0 eine neue Policy greift (Name fällt mir gerade nicht mehr ein), die den Abbruch des Main Thread durch Exceptions in separaten Threads verhindert.

Mit Tag(s) versehen: , ,

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: