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:
Wenn man sich die Details anschaut, sieht man folgendes:
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.