Tipps & Tricks: Testcode sollte immer in #ifdef _DEBUG #endif Blöcke integriert sein!

Dieser Tipp hört sich trivial an, aber wenn man sich nicht dran hält erlebt man übelste Überraschungen.
Nur zu oft muss man während der Entwicklung oder bei der Fehlersuche Code einbauen, der Test, Ausgaben, Verzögerungen oder sonstige Operationen ausführt, die mir als Entwickler helfen ein Problem zu finden, oder einer Lösung für eine verzwickte Frage zu lösen. Um so größer das Problem wird um so mehr Stellen werden oft verändert.
Es bleibt die Frage ob sich jeder Entwickler noch erinnert wo er überall etwas für Testzwecke eingebaut hat.

Das üble Ergebnis ist, dass man manchmal toten nutzlosen, Performance fressenden Code ausliefert. Oder gar Code ausliefert, der evtl. zu neuen Fehlern führt. Da muss nur ein einfacher DebugBreak im Code zurückbleiben und schon crashed die Anwendung sauber beim Kunden…

Testcode sollte grundsätzlich in einem #ifdef Block eingebaut werden. Und Code der wirklich nur für Tests vorhanden ist und er sogar später in der Debug Version des Programmes nichts zu suchen hat sollte mit einem #else #error versehen werden. Ein ASSERT kann einen viel abnehmen um so etwas zu vermeiden, aber sogar mancher ASSERT  ist später überflüssig und behindert auch Tests in der Debug Version.

So habe ich in unserer Software einen Sleep(100); 😮 gefunden, der von einem Entwickler eingebaut wurde, um einen Crash in einem komplexen Kommunikationsproblem zwischen mehreren Threads zu finden.Er hat den Fehler gefunden, den Sleep aber nie wieder entfernt.
Hätte mein Kollege sich an meine Spezifikationen gehalten, hätten wir nicht nachträglich auf die mühsame Suche gehen müssen wo unsere Performanceverluste bei 5% Prozessorlast herkommen. So wäre das Ganze schon beim Release-Build aufgefallen:

#ifdef _DEBUG
// Test Sleep to find cross thread problems for bug#234
Sleep(100);
#else
#error Remove test condition here. Just used to find bug#234 
#endif

BTW: Ich verwende deshalb immer ein Code-Snippet über mein VisualAssist X, der mir einen entsprechenden Codeblock einsetzt.

5 Gedanken zu „Tipps & Tricks: Testcode sollte immer in #ifdef _DEBUG #endif Blöcke integriert sein!“

  1. Wohl wahr, Debugcode in Produktivumgebungen kann fiese Auswirkungen haben… Zum Bleistift wenn man in eine Logdatei schreibt, dessen Pfad nur auf dem Entwicklerrechner existiert -> Ausstieg beim Anwender. 🙁

    Und für die .NET-Entwickler noch ein Tip hinterher: Mit dem ConditionalAttribute kann man ganze Methoden deklarieren, die nur bei Debug-Builds erstellt und aufgerufen werden. Voraussetzung ist nur, daß sie keine Rückgabe liefern.

    [Conditional(„DEBUG“)]
    private static void HeavyDebuggingAndLogging()
    {
    // do some stuff…
    }

  2. Guter Tipp, Danke!

    Du hast dein Code-Snippet for VAssistX erwähnt, könntest du das nicht auch posten?

    Ich geb zu, es ist mir auch schon passiert dass Debug-Code in einen Release reingerutscht ist. Aber seitdem ich Versionskontrolle habe und nutze, passiert mir das nicht mehr. Vor jedem einchecken mach ich jeweils ein Diff, da sehe ich dann sofort, ob ich noch irgendwelchen Debug-Code vergessen habe zu entfernen.

  3. Das ist kein Geheimnis. Er steht ja oben schon als Beispiel:
    #ifdef _DEBUG
    $end$
    #else
    #error Remove test condition for final build
    #endif

    Ein entsprechenender #ifdef _DEBUG ist sowieso schon bei den Samples. Ich habe einfach nur noch den #else case ergänzt. Der verhindert dass ein Releasebuild läuft mit diesem entsprechenden Code.

    Das mit dem Diff klappt oft genug nicht. Ich habe gerade einen Checkin gemacht der leider Änderungen in 14 Dateien umfasste und die Änderungen waren wirklicht nicht wenig. Ich glaube kaum dass unter 250 neuen Codezeilen so ein destruktiver Sleep auffallen würde den ich erwähnt habe.

  4. Hallo zusammen,

    schließe mich voll dem Tipp von Stefan an. Meistens ist der code den man beim debuggen eines konkreten Problems einfügt so spezifisch, dass man ihn als temporär betrachten sollte. Andernfalls wird der code ja mit der Zeit immer aufgeblähter.
    Ich habe mit dem Durchschauen der diffs vor einem checkin auch gute Erfahrungen gemacht. Im Vergleich zum Gesamtaufwand der Änderung ist der Aufwand dafür vernachlässigbar. Und vergleich ihn mal mit dem Aufwand für die Fehlersuche und Kundenprobleme von denen Du geschrieben hast 🙂

    Viele Grüße,
    Andreas

  5. Das eine schließt ja das andere nicht aus.

    Mit entsprechenden #ifdef Blöcken würde ein Releasebuild sich ja gar nicht erst erzeugen lassen. Das ist ja genau was Du meinst mit „dass man ihn als temporär betrachten sollte!“.
    Dafür sorgt eben der #ifdef _DEBUG Block.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.