13 Stimmen

TDD : Irgendein Muster für konstante Tests?

Konstanten sind wunderbare Menschen - sie können an einer einzigen Stelle einen Wert speichern, der überall in Ihrem Code verwendet wird. Das Ändern dieses Wertes erfordert nur eine einfache Änderung.

Das Leben ist cool.

Nun, das ist das Versprechen. Die Realität ist manchmal anders:

  • Sie ändern die LogCompleteFileName konstanter Wert aus L:\LOGS\MyApp.log a \\Traces\App208.txt und Sie erhalten zwei Dateien : \\traces\App208.txt für die Spuren und \\traces\App208.txt.log für die Protokolle...
  • Sie ändern TransactionTimeout von 2 auf 4 Minuten zu ändern, und Sie erhalten immer noch eine Zeitüberschreitung nach 2 Minuten (nachdem Sie den Tag damit verbracht haben, herauszufinden, dass Sie auch die Zeitüberschreitung des DBMS und die Zeitüberschreitung des Transaktionsmanagers ändern müssen...).
  • Sie ersetzen SleepTimeInMinutes de 1 a 10 und Sie sehen keine Veränderung (nach etwa einer Stunde stellen Sie fest, dass der Name der Konstante irreführend war: die Granularität ist nicht die Minute, sondern die Millisekunde...).
  • Noch subtiler: Sie ändern CompanyName von, sagen wir Yahoo a Microsoft aber automatische E-Mail-Benachrichtigungen werden immer noch gesendet an alert@yahoo.com ...

Die Erstellung einer Konstante ist ein Vertrag. Sie teilen Ihren Lesern mit, dass der Wert, wenn sie ihn ändern, immer noch so funktioniert, wie sie denken, dass er sein sollte.

Nicht weniger.

Natürlich müssen Sie prüfen, ob Sie Ihre Leser nicht in die Irre führen. Sie müssen sicherstellen, dass der implizierte Vertrag richtig ist.

Wie kann man das mit TDD erreichen? Damit komme ich einfach nicht weiter. Die einzige Möglichkeit, wie ich eine Änderung für einen konstanten (!) Wert testen kann, ist, diese Konstante zu einer Anwendungseinstellung zu machen... Sollte ich zu dem Schluss kommen, dass die const Schlüsselwort vermieden werden sollte, wenn ich denke, dass sich der Wert ändern kann und wird?

Wie testen Sie Ihre (so genannten) Konstanten mit TDD?

Vielen Dank im Voraus :)

13voto

Jamie Ide Punkte 46985

Die einzige Möglichkeit, eine Änderung für einen konstanten (!) Wert zu testen, besteht darin, diese Konstante zu einer Anwendungseinstellung zu machen

Alle von Ihnen in der Frage aufgeführten Verwendungen klingen für mich nach Anwendungseinstellungen und nicht nach Konstanten. Eine Konstante ist ein Wert, der, nun ja, konstant ist, wie zum Beispiel:

const decimal LITERS_PER_HOGSHEAD = 238.480942392;

Nachtrag: Hoffentlich ist dies hilfreicher als meine leichtfertige Antwort. Ich erstelle in der Regel eine AppSettings-Klasse. Einige der Eigenschaften in dieser Klasse werden aus der Konfigurationsdatei gezogen, einige sind Einstellungen, von denen ich nicht erwarte, dass sie sich ändern, und einige könnten Konstanten sein.

public class AppSettings
{
    public const decimal GILLS_PER_HOMER = 1859.771248601;

    public string HelpdeskPhone
    {
        get { // pulled from config and cached at startup }
    }

    public int MaxNumberOfItemsInAComboBox
    {
        get { return 3; }
    }
}

0 Stimmen

Ich denke, dass dies letztendlich der richtige Weg sein könnte. Aber von Zeit zu Zeit erstelle ich Konstanten aus Gründen der Lesbarkeit oder einfach, weil ich denke, dass sich der Wert in absehbarer Zeit nicht ändern wird, aber es kann sein! Beispiel: MaxNumberOfItemsInAComboBox... Ich zögere, eine Anwendungseinstellung für solche Werte zu erstellen. Aber ich muss sicherstellen, dass meine Anwendung immer noch funktioniert, wenn jemand sie ändert... Vielen Dank für Ihre Antwort :)

0 Stimmen

+1 für die konstanten Namen. Und die Antwort auch... aber GILLS_PER_HOMER hat es übertrieben.

0 Stimmen

Hey, gibt es eine Chance zu erfahren, was Gill_Per_Homer bedeutet? Sorry, ich bin Franzose, wie ein Haufen Stackoverflower :)

5voto

Esko Luontola Punkte 71758

Es gibt zwei Arten von Konstanten:

1) Konstanten für Bequemlichkeit/Lesbarkeit

Beim Schreiben von Code mit TDD, sollte jede Zeile der Produktion Code existiert, weil zuerst gab es einen fehlgeschlagenen Test, die erforderlich, dass Code geschrieben werden. Beim Refactoring des Codes werden einige magische Werte in Konstanten umgewandelt. Einige dieser Werte könnten auch als Anwendungseinstellungen gut sein, aber der Einfachheit halber (weniger Code) wurden sie im Code konfiguriert, anstatt in einer externen Konfigurationsdatei.

In diesem Fall werden bei der Art, wie ich Tests schreibe, sowohl der Produktions- als auch der Testcode dieselben Konstanten verwenden. In den Tests wird angegeben, dass die Konstanten wie erwartet verwendet werden. Aber die Tests wiederholen nicht den Wert einer Konstante, wie z. B. in " assert MAX_ITEMS == 4 ", da dies doppelten Code bedeuten würde. Stattdessen wird in den Tests geprüft, ob ein bestimmtes Verhalten die Konstanten korrekt verwendet.

Zum Beispiel, aquí ist eine (von mir geschriebene) Beispielanwendung, die eine Langkatze einer bestimmten Länge. Wie Sie sehen, ist der Longcat als eine Reihe von Konstanten definiert:

public class Longcat {

    // Source: http://encyclopediadramatica.com/Longcat

    public static final String HEAD_LINES = "" +
            "    /\\___/\\         \n" +
            "   /       \\         \n" +
            "  |  #    # |         \n" +
            "  \\     @   |        \n" +
            "   \\   _|_ /         \n" +
            "   /       \\______   \n" +
            "  / _______ ___   \\  \n" +
            "  |_____   \\   \\__/ \n" +
            "   |    \\__/         \n";
    public static final String BODY_LINE = "" +
            "   |       |          \n";
    public static final String FEET_LINES = "" +
            "   /        \\        \n" +
            "  /   ____   \\       \n" +
            "  |  /    \\  |       \n" +
            "  | |      | |        \n" +
            " /  |      |  \\      \n" +
            " \\__/      \\__/     \n";
...

Die Tests überprüfen, ob die Konstanten korrekt verwendet werden, aber sie duplizieren nicht den Wert der Konstante. Wenn ich den Wert einer Konstante ändere, verwenden alle Tests automatisch den neuen Wert. (Und ob die Longcat-ASCII-Grafik richtig aussieht, muss manuell überprüft werden. Obwohl man das sogar mit einem Abnahmetest automatisieren könnte, was bei einem größeren Projekt empfehlenswert wäre.)

    public void test__Longcat_with_body_size_2() {
        Longcat longcat = factory.createLongcat(2);
        assertEquals(Longcat.BODY_LINE + Longcat.BODY_LINE, longcat.getBody());
    }

    public void test__Fully_assembled_longcat() {
        Longcat longcat = factory.createLongcat(2);
        assertEquals(Longcat.HEAD_LINES + longcat.getBody() + Longcat.FEET_LINES, longcat.toString());
    }

2) Universelle Konstanten

Dieselbe Anwendung hat auch einige Konstanten, die niemals geändert werden sollen, wie z. B. das Verhältnis zwischen Metern und Fuß. Diese Werte können/sollten im Test fest kodiert werden:

    public void test__Identity_conversion() {
        int feet1 = 10000;
        int feet2 = FEET.from(feet1, FEET);
        assertEquals(feet1, feet2);
    }

    public void test__Convert_feet_to_meters() {
        int feet = 10000;
        int meters = METERS.from(feet, FEET);
        assertEquals(3048, meters);
    }

    public void test__Convert_meters_to_feet() {
        int meters = 3048;
        int feet = FEET.from(meters, METERS);
        assertEquals(10000, feet);
    }

Und der Produktionscode sieht so aus:

public enum LengthUnit {

    METERS("m", 1.0), FEET("ft", 0.3048), PETRONAS("petronas", 451.9), LINES("lines", 0.009);

    private final String name;
    private final double lengthInMeters;
...

    public int from(int length, LengthUnit unit) {
        return (int) (length * unit.lengthInMeters / this.lengthInMeters);
    }
}

Beachten Sie, dass ich keine Tests für die Höhe der Petronas-Zwillingstürme weil diese Informationen deklarativ sind (und deklarative Daten selten kaputt gehen) und ich bereits Tests für die Konvertierungslogik geschrieben hatte (siehe oben). Wenn weitere ähnliche Konstanten hinzugefügt werden (Eiffelturm, Empire State Building usw.), werden sie von der Anwendung automatisch gefunden und funktionieren wie erwartet.

3voto

Otávio Décio Punkte 72052

Nach dem, was ich in Ihrer Frage gelesen habe, hat dies nichts mit TDD zu tun. Die von Ihnen beschriebene Verwendung ist keine echte Konstante, sondern eher ein Konfigurationswert, so dass Sie in diesen Fällen nicht die const Modifikator.

0 Stimmen

Ok, aber was ist mit den "wahren Konstanten"? Wie können wir sicherstellen, dass irgendjemand sie ändern kann, ohne dass wir alles testen müssen? Wenn er oder sie alles testen muss, sollten wir dann eine Konstante verwenden? Ohne Konstante ist die Botschaft klar: Leute, ihr habt kein Glück! Du musst den ganzen Quellcode durchsuchen und alles testen. Eine Konstante zu erstellen ist ein Vertrag, wie stellt man sicher, dass er in Ordnung ist? Vielen Dank für Ihre Antwort :)

0 Stimmen

Wenn Sie TDD verwenden, ist das Testen von allem ein trivialer Tastendruck. Eine Konstante sollte gar nicht erst existieren, es sei denn, ein Test ist fehlgeschlagen, der die Erstellung der Konstante erforderlich machte. Wenn beim Ausführen der Tests nach dem Ändern der Konstante nichts kaputt geht, funktioniert alles so, wie es in den Tests angegeben ist.

0 Stimmen

"Wahre Konstante"? PI? Sie kann sich niemals ändern. Die Tests für Konstanten oder Konfigurationsparameter sind trivial. Das Design-Problem ist das, womit Sie Probleme haben - die Leute haben die Dinge nicht so entworfen, dass sie die Konfigurationsparameter so verwenden, wie Sie denken, dass sie verwendet werden sollten. Das ist nicht TDD. Das ist Design.

3voto

fortran Punkte 70744

Das liegt daran, dass all diese Dinge keine Konstanten sind... Dinge, die es tatsächlich gibt:

  • G (6,67300 × 10-11 m3 kg-1 s-2)
  • c (299 792 458 m / s)
  • pi (3,1415926535897931)
  • L (6,0221415 × 1023 mol-1)
  • ...

Sollte sich eines dieser Dinge jemals ändern, machen Sie sich keine Sorgen, der Absturz Ihrer Anwendung wird das Letzte sein, was zählt xD

0 Stimmen

Ich wusste, dass es jemandem auffallen würde! xD

1voto

Johnno Nolan Punkte 28357

Ein paar Dinge.

Erstens TDD und das emergente Design, das TDD fördert, ist die Trennung der Verantwortlichkeiten, die Anwendung von DRY und Dependency Injection.

Eine Konstante zu testen ist einfach und vielleicht ein wenig sinnlos.

Aber die Bewertung dieser Konstante durch eine andere Einheit zu testen, ist meiner Meinung nach keine Einheitstest . Es ist ein Integrationstest . Das Testen des Konstantenwerts in anderen Einheiten würde durch einen Mock oder Stub abgedeckt.

Zweitens sind Ihre Beispiele vielfältig:

Ihr Protokollbeispiel verwendet nur 1 Datei. Es ist nur, dass 2 existieren. Wenn die Anforderung war für nur eine Datei zu existieren, dann könnten Sie einen Test für das herzustellen.

Das Testen der Transaktionszeitüberschreitung sollte von einem Integrationstest aufgegriffen werden. Dies sollte zeigen, dass das Problem nicht bei Ihrem ursprünglichen Test liegt.

Die Änderung des Firmennamens ist in Ordnung, da sie sich auf den Firmennamen bezieht. Der Domänenname sollte und könnte eine andere Konstante sein.

Wie andere bereits erwähnt haben, kann die Weitergabe einer Konfigurationsklasse hilfreich sein, um beim Testen anderer Klassen zu spiegeln.

1 Stimmen

Ich schätze Ihren Kommentar sehr. Aber ich hatte wirklich schlechte Zeiten (alle zwei Tage), um konstante Werte zu ändern... Wenn Sie nicht sicherstellen können, dass Ihre Konstanten geändert werden können, sollten Sie meiner Meinung nach besser Literale verwenden! Ich bin dann viel skeptischer und werde den gesamten Code überprüfen. Ich habe so viel Zeit damit verbracht, herauszufinden, warum das Ändern einer Konstante nicht den erwarteten Effekt hatte... Vielen Dank für Ihre Antwort.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X