Wir haben einige Komponententests, die fehlschlagen, wenn sie im Freigabemodus oder im Debug-Modus ausgeführt werden. Wenn ich einen Debugger im Freigabemodus anschließe, werden die Tests bestanden. Es gibt viel zu viel Code, um hier zu veröffentlichen, also suche ich wirklich nur nach Best Practices beim Debuggen von Release-Modus-Problemen. Ich habe nach:
gesuchtLÖSUNG: In diesem Fall ist es, weil ich Fließkommavariablen für Gleichheit vergleiche. Ich konnte die Gleitkommawerte nicht ohne großen Refactoring in Dezimalzahlen ändern, also fügte ich eine Erweiterungsmethode hinzu:
%Vor%Da es scheinbar fließend ist, gibt es so viele Dinge, die schief gehen können. Sehen: C # - Inkonsistente mathematische Operation führt zu 32-Bit und 64-Bit und Probleme mit doppelter Genauigkeit unter .NET
Es gibt so viele Dinge, die mit Fließkommazahlen verworfen werden können. Und das Vergleichen von Schwimmern für die Gleichheit ist ein generelles Nein-Nein. Sie können den Unterschied kleiner als ein vernünftiges Epsilon überprüfen.
Eine Sache, die das angezeigte Verhalten verursachen könnte, ist ein Fehler, der eine Wettlaufbedingung verursacht. Durch das Hinzufügen eines Debuggers kann das Timing des Codes so geändert werden, dass die Race-Bedingung nicht mehr ausgelöst wird.
Um es zu beheben, verwenden Sie die Synchronisierung immer dann, wenn mehrere Threads auf Daten zugreifen.
Ich vergleiche einige Float-Werte in der IsEqual-Methode.
Das klingt nach einer sehr schlechten Idee. Sie sollten Floats nicht mit Gleichheit vergleichen, da Fließkomma-Berechnungen nicht zu 100% präzise sind und Sie Repräsentations- und Rundungsfehler erhalten können. Vergleichen Sie, um festzustellen, ob sie ausreichend nahe beieinander liegen. Bei Berechnungen mit Geld möchten Sie wahrscheinlich stattdessen den decimal
-Typ verwenden.
Fragen, die Sie sich stellen sollten -
Nummer 3 ist in diesem Fall ein sehr wahrscheinlicher Bad Boy. Die Garbage-Collection kann beim Debuggen und Freigeben sehr unterschiedlich sein. Wenn ein Garbage Collection-Objekt erfasst wird, wirkt sich dies möglicherweise auf das Ergebnis eines späteren Unit-Tests aus.
Und zu guter Letzt, wenn Sie NUnit und TestDriven.NET verwenden - die beiden führen Tests in verschiedenen Ordnungen durch
Dies ist oft der Fall, da das Debug-Build standardmäßig nicht optimiert ist. Selbst wenn Sie es aktivieren, ist das Verhalten beim Debugging sehr unterschiedlich. Sie können den "Code optimieren" in den Projekteinstellungen für alle Assemblys auf der Registerkarte Eigenschaften- & gt; Erstellen deaktivieren.
Es gibt sicherlich noch andere Änderungen, die zu Unterschieden führen können. Diese habe ich selten als Ursache von Problemen gefunden, für mich ist es fast immer der Optimierer.
Klassische Fehler des Optimierers schließen Methoden ein, die "inline" erhalten, so dass sie nicht auf einem Call-Stack erscheinen. Dies führt zu Problemen bei der Verwendung von System.Diagnostics.StackFrame-Klassen zum Ermitteln des aktuellen Ausführungspunkts. In ähnlicher Weise beeinflusst dies das Ergebnis von MethodBase.GetCurrentMethod oder anderen Funktionen / Verhalten, die auf der ausführenden Methode beruhen.
Dann gibt es natürlich viele Dinge, die ich vom Optimierer gesehen habe, die ich einfach nicht erklären kann. Ein solches Beispiel wurde dokumentiert und diskutiert in einem Post ' HashDerivedBytes - ersetzt Rfc2898DeriveBytes, aber warum? ' aber ich habe das nie gelöst Geheimnis. Ich weiß nur, dass der Optimierer Rfc2898DeriveBytes nur flach brach, wenn er verwendet wurde, um eine Reihe von abgeleiteten Bytes zu erzeugen. Seltsamerweise brach dies nur, wenn die erzeugten Bytes nicht gleichmäßig durch die Größe des verwendeten Hash-Algorithmus teilbar waren (20) und nur falsche Ergebnisse nach den ersten 20 Bytes erzeugten.
Fakt ist, dass Optimierungen, die den Code beeinträchtigen, für Compiler keine neue Sache sind. Die meisten C ++ - Entwickler der alten Schule werden dir das sofort erzählen und dann, wie ich es getan habe, in eine langatmige Geschichte darüber gehen, wie sie damit umgehen;)
Wie Mark vermutet, ist dies in der Regel auf ein Timing-Problem zurückzuführen, oft auf ein Race Condition- oder Synchronisationsproblem.
Ein gängiger Weg, um diese Art von Problem zu lösen, ist die Verwendung von "print" -Anweisungen in den betroffenen Bereichen, um Ihnen zu zeigen, was vor sich geht. Wenn die print-Anweisungen ( Console.WriteLine
, Response.Write
, Protokollierung oder was auch immer) das Problem verschwinden lassen, speichern Sie die Werte in globalen Variablen und drucken Sie die Globals, sobald das Problem auftaucht.
Die letzte Zeit, die mir passiert ist, war Code, der von einer seriellen Schnittstelle gelesen hat. Die Debugging-Aktivität führte gerade zu einer Änderung des Timings, die sich auf die Pufferung der Bytes aus dem seriellen Port auswirkte. Dadurch wurde die Analyse des Puffers geändert. Da die Druckanweisungen das Timing änderten, musste ich die Daten bis zur Ausgabe speichern.
Um nur meine zwei Cent dazu hinzuzufügen, habe ich kürzlich festgestellt, dass ich einen Datumsvergleich in einer SQL-Prozedur hatte, die der Test aufgerufen hat. Die Daten wurden alle zuvor in der Testprozedur automatisch generiert, und Werte wurden in die Datenbank eingefügt. Daher waren sie gelegentlich genau gleich (wenn RunTests verwendet wurden), wodurch bei einem Tabellenjoin eine Null zurückgegeben wurde. Nicht was ich erwartet hatte. Offensichtlich wird es im Debug-Modus einen Unterschied in den automatisch generierten Zeiten geben, was bedeutet, dass ich nie auf den Fehler stieß. Ich habe das gelöst, indem ich
eingefügt habeThreading.Thread.Sleep (520)
wo immer es eine Verzögerung zwischen den Aktionen geben würde. Problem gelöst.