2013-02-26 3 views
12

Ich schreibe einen dünnen Wrapper um das Wörterbuch, das thread-sicher entworfen wurde. Daher sind einige Sperren erforderlich, und der Großteil der Logik besteht darin, sicherzustellen, dass Dinge ordnungsgemäß gesperrt sind und threadsicher darauf zugegriffen wird.Ist es möglich, herumzuspielen/gefälscht zu werden, um ein fehlendes Schloss zu einem fehlerhaften Test zu machen?

Jetzt versuche ich es zu testen. Eine große Sache, die ich gern testen würde, ist das Sperrverhalten, um sicherzustellen, dass es korrekt ist. Allerdings habe ich das nie irgendwo gesehen, also bin ich mir nicht sicher, wie ich das anstellen soll. Außerdem weiß ich, dass ich einfach eine Menge Fäden benutzen kann, um Zeug an die Wand zu werfen, aber bei dieser Art von Test gibt es keine Garantie, dass es scheitern wird, wenn es falsch ist. Es liegt an dem vom Betriebssystem definierten Verhalten bei der Thread-Planung.

Welche Möglichkeiten gibt es, um sicherzustellen, dass mein Blockierverhalten bei Komponententests korrekt ist?

+5

+1 auf das Konzept der Frage konsistent zu duplizieren; neugierig, warum Sie für diesen speziellen Fall kein 'ConcurrentDictionary' verwenden. –

+0

@TimMedora portable Klassenbibliothek Targeting Windows Phone :( – Earlz

+0

Verstanden. Aus meiner Erfahrung wird Bombardierung einer vermutlichen Thread-Safe-Klasse mit Tausenden von Operationen (Cross-Thread, abwechslungsreiche Reihenfolge) schnell Probleme aufdecken, aber das ist natürlich nicht deterministisch könnte ein paar interne Test-Hooks hinzufügen, um das gewünschte Szenario zu erzwingen –

Antwort

1

Ich glaube nicht, mit Dictionary selbst können Sie zuverlässige Tests erreichen - Ihr Ziel ist es, 2 Anrufe parallel zu laufen, was nicht zuverlässig passieren wird.

Sie können ein benutzerdefiniertes Dictionary verwenden (d. H. Von einem regulären Dictionary ableiten) und Callbacks für alle Get/Add-Methoden hinzufügen. Dann können Sie Anrufe in 2 Threads nach Bedarf verzögern ... Sie benötigen eine separate Synchronisierung zwischen 2 Threads, damit Ihr Testcode so läuft, wie Sie wollen und nicht die ganze Zeit blockieren.

+0

Ich endete damit. Ich habe genau ausgeführt, wie ich das gemacht habe unter – Earlz

3

Sperren ist nur ein Implementierungsdetail. Sie sollten die Race Condition selbst ausprobieren und testen, ob die Daten im Test nicht ihre Integrität verloren haben.

Dies wäre ein Vorteil, wenn Sie die Implementierung Ihres Wörterbuchs ändern und andere Synchronisierungsprimitive verwenden möchten.

Das Testen der Sperrung zeigt, dass Sie eine Sperranweisung aufrufen, wo Sie diese erwarten. Während Tests mit einer gespotteten Race-Bedingung können Orte aufdecken, die Sie nicht erwarten zu synchronisieren.

+0

Das beruht im Grunde auf Dictionary brechen, wenn es gleichzeitig zugegriffen – Earlz

+0

Nein sollte es in einem korrekten Zustand bleiben, dass würde durch deinen Test bewiesen werden. – Kimi

+2

Nun, es basiert auf Implementierungsdetails von Dictionary dann. Ich muss tatsächlich testen, dass Wörterbuch nie versucht wird, auf eine gleichzeitige Weise zugegriffen zu werden, weil die ganze Sache für meine Zwecke nicht threadsicher ist. – Earlz

2

Ich endete im Grunde zu tun, was @Alexei sagte. Um genauer zu sein, ist es das, was ich getan habe:

  1. ein PausingDictionary machen, die im Grunde nur einen Rückruf für jede Methode hat, aber ansonsten geht nur durch eine regelmäßige Wörterbuch
  2. Zusammenfassung meines Code (DI verwenden, etc), so dass ich PausingDictionary anstelle eines regulären Wörterbuchs beim Testen verwenden kann
  3. Hinzugefügt zwei ConcurrentDictionaries in meinem Komponententest. Einer namens "Accessed" und einer namens "GoAhead". Der Schlüssel zum thiis ist eine Kombination aus "action"+Thread.GetHashCode().ToString() (wo Aktion für jeden Rückruf unterscheidet)
  4. Initialized alles falsch, und fügte einige Erweiterungsmethoden mit ihm, um die Arbeit ein wenig zu erleichtern
  5. -Setup die Rückrufe des Wörterbuch für die set Accessed thread to true, aber es würde dann im Callback warten, bis GoAhead true war
  6. Begann zwei Threads aus dem Unit-Test. Ein Thread würde auf das Wörterbuch zugreifen, aber da GoAhead für diesen Thread falsch ist, würde er dort sitzen. Der zweite Thread würde dann auch versuchen, auf das Wörterbuch zuzugreifen
  7. Ich würde eine Behauptung haben, dass Accessed für diesen Thread falsch ist, weil mein Code es aussperren sollte.

Es gibt ein bisschen mehr als das. Ich müsste auch eine IList nachmachen, aber ich glaube nicht, dass ich das tun werde.Diese Komponententests, obwohl wertvoll, sind definitiv nicht die einfachste Sache auf der Welt zu schreiben. Abgesehen von Setup-Code und gefälschten Interface-Implementierungen und so, endet jeder Test etwa 25 Zeilen Not-Boiler-Code. Sperren ist schwer. Der Beweis, dass Ihre Verriegelung effektiv ist, ist noch schwieriger. Erstaunlicherweise können Sie mit dieser Art von Muster fast jedes Szenario testen. Aber es ist sehr ausführlich und macht keine schönen Tests

Also, obwohl es schwer ist, die Tests zu schreiben, funktioniert das perfekt. Wenn ich eine Sperre entferne, ist das durchweg fehlgeschlagen, und wenn ich die Sperre wieder hinzufüge, passiert sie beständig.

Edit:

Ich denke, diese Methode der „Steuerung Verschachtelung“ von Threads würde es auch ermöglichen, Thread-Sicherheit zu testen, da Sie einen Test für jede mögliche Verschachtelung schreiben. Mit etwas Code wäre das unmöglich, aber ich möchte nur sagen, das ist in keiner Weise auf den Sperrcode beschränkt. Sie können die gleiche Methode verwenden, um einen thread-sicheren Fehler wie foo.Contains(x) und dann var tmp=foo[x]

+0

+1: sehr schöne write-up! –

Verwandte Themen