Frage

Für Unit-Tests muss ich eine Netzwerkantwort nachbilden.Die Antwort ist normalerweise ein Bytestrom, der als gespeichert wird const vector<uint8_t>.Für den Unit-Test möchte ich jedoch den Vektor mit Daten erstellen, die entweder in der CPP-Datei fest codiert sind oder aus einer Datei in derselben Lösung gelesen werden.Meine Beispieldaten sind etwa 6 KB groß.Was sind die allgemeinen Richtlinien zum Platzieren von Daten bei der Verwendung? Googletest?

War es hilfreich?

Lösung

Vielleicht (a) benötigen Sie eine große Folge von Daten für eine Rolle, in der Testfälle sie gerade lesen.Dabei kann es sich auch um (klassen-)globale Daten handeln, mitconst Zugang.

Vielleicht (b) benötigen Sie eine große Folge von Daten für eine Rolle, in der Testfälle sie lesen und ändern oder zerstören.Dies muss pro Testfall reninitialisiert werden und nicht habenconst Zugang.

Vielleicht beides.In jedem Fall würde eine konventionelle Googletest -Implementierung a verwenden TestvorrichtungUm die Daten zu verkapulieren Setup() Mitgliedsfunktion und greifen Sie über eine Getter -Methode des Geräts darauf zu.

Das folgende Programm zeigt ein Gerät, das sowohl mutable Daten pro Fall als auch globale konstante Daten liefert, die von Dateien erfasst wurden.

#include <vector>
#include <fstream>
#include <stdexcept>
#include "gtest/gtest.h"

class foo_test : public ::testing::Test
{
protected:
    virtual void SetUp() {
        std::ifstream in("path/to/case_data");
        if (!in) {
            throw std::runtime_error("Could not open \"path/to/case_data\" for input");
        }
        _case_data.assign(
            std::istream_iterator<char>(in),std::istream_iterator<char>());
        if (_global_data.empty()) {
            std::ifstream in("path/to/global_data");
            if (!in) {
                throw std::runtime_error(
                    "Could not open \"path/to/global_data\" for input");
            }
            _global_data.assign(
                std::istream_iterator<char>(in),std::istream_iterator<char>());
        }
    }
    // virtual void TearDown() {}   
    std::vector<char> & case_data() {
        return _case_data;
    }
    static std::vector<char> const & global_data() {
        return _global_data;
    }

private:
    std::vector<char> _case_data;
    static std::vector<char> _global_data;

};

std::vector<char> foo_test::_global_data;

TEST_F(foo_test, CaseDataWipe) {
  EXPECT_GT(case_data().size(),0);
  case_data().resize(0);
  EXPECT_EQ(case_data().size(),0);
}

TEST_F(foo_test, CaseDataTrunc) {
  EXPECT_GT(case_data().size(),0);
  case_data().resize(1);
  EXPECT_EQ(case_data().size(),1);
}

TEST_F(foo_test, HaveGlobalData) {
  EXPECT_GT(global_data().size(),0);
}


int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Für Fall (a) könnten Sie auch in Betracht ziehen, die Daten in a zu erfassen globales SetupMitgliedsfunktion durch Unterklassenbildung ::testing::Environment, aber ich sehe keinen allgemeinen Grund, es vorzuziehen, es so zu tun.

...Oder fest codieren?

Dann zur Frage, ob die Testdaten überhaupt in einer Datei aufbewahrt werden sollen oder sie in der Testquelle codieren. Leser, die an dieser Stelle zufrieden sind, werden sich von nun an nur noch langweilen.

Allgemeines Thema ist dies eine Frage der Beurteilung im Bereich, und ich glaube nicht, dass die Verwendung von Googletest die Waage materiell tippt.Ich denke, die Hauptüberlegung ist: Ist es wünschenswert, einen Testdatenstück variieren zu können, ohne den Test-Suite wieder aufzubauen?

Angenommen, der Wiederaufbau der Testsuite, um zu variieren. Dieser Artikel ist ein nicht zuverständlicher Kosten und Sie gehen davon aus, dass der Inhalt des Artikels in Zukunft unabhängig vom damit verbundenen Testcode variieren wird.Oder es kann unabhängig vom zugehörigen Testcode für verschiedene Konfigurationen des zu testenden Systems variieren.In diesem Fall erhalten Sie am besten das Element aus einer Datei oder einer anderen Quelle, die durch Laufzeitparameter der Testsuite ausgewählt werden kann.In Googletest Unterklassen class ::testing::Environment ist eine entworfene Einrichtung zum parametrisierten Erwerb von Testsuite -Ressourcen.

Wenn in Wirklichkeit Der Inhalt eines Testdatenelements ist locker mit dem zugehörigen Testcode verbunden, und es ist höchst unwahrscheinlich, dass es eine umsichtige Wahl ist.(Und Testdateien haben im Gegensatz zu anderen Arten von Laufzeitkonfiguratoren die wertvolle Eigenschaft, dass sie im selben System wie Quellcode Version kontrolliert werden können.)

Wenn der Inhalt eines Testdatenelements Ist fest mit dem zugehörigen Testcode festgekoppelt, dann bin ich verzerrt, um ihn zu harten, anstatt ihn aus einer Datendatei zu extrahieren.Nur voreingenommen, nicht dogmatisch engagiert.Möglicherweise verwendet Ihre Testsuite robuste Bibliothekseinrichtungen für die Initialisierung öffentlicher API-Testdaten aus XML-Dateien, die auch in Systeme mit Testmanagement und Defektmanagement einbezogen werden.Bußgeld!

Ich halte es so eindeutig wünschenswert, dass, wenn eine Datei mit Testdaten eine primäre Testressource ist - eine, die die Testsuite nicht generieren kann -, dass der Inhalt am besten Textdaten sind, die ein kompetenter Betreuer leicht verstehen und manipulieren kann.In dieser Umgebung würde ich natürlich bedenken, dass beispielsweise eine Liste von C/C ++ - Hex -Konstanten ist Textdaten - Es ist Quellcode.Wenn eine Testdatei binäre oder entmutigend maschinenorientierte Daten enthält, enthielt die Testsuite am besten die Mittel der Produktion aus lesbaren Primärressourcen.Es ist manchmal unvermeidbar, dass eine Testsuite von "archetypischen" Binärdateien extern bezogen wird, aber sie beinhalten fast zwangsläufig das grimmige Spektakel von Testingenieuren und Bug-Fixern, die sich vor den Hex-Editoren grau machen.

Angesichts des Prinzips, dass Primärtestdaten für die Betreuer lesbar sein sollten, können wir es als Norm betrachten, dass Primärtestdaten "eine Art Code" sind:Es wird logischfrei sein, aber es wird die Art von Textmaterial sein, an die Programmierer an Vermessung und Bearbeitung gewöhnt sind.

Stellen Sie sich vor, eine bestimmte Abfolge von 4096 64-Bit-nicht signierten Ganzzahlen (der Big Magic Table) ist zum Testen Ihrer Software erforderlich und ist eng mit dem zugehörigen Testcode verbunden.Es könnte in einer Quelldatei der Testsuite als riesige Vektor- oder Array-Initialisiererliste fest codiert werden.Es könnte durch die Testsuite aus einer Datendatei extrahiert werden, die im CSV-Format oder in CSV-gepunkten Zeilen gehalten wird.

Für die Extraktion aus einer Datendatei und gegen hartnäckige Kodierung kann sie aufgefordert werden (gemäß Andrew McDonells Antwort), dass dies wertvoll eine Entwirrung von Überarbeitungen an die BMT durch Revisionen eines anderen Code in derselben Quelldatei erzielt.Ebenso könnte es darauf gedrängt werden, dass jeder Quellcode, der enorme wörtliche Initialisierungen rahmen, tendenziell nicht überrascht und daher eine Wartungshaftung.

Beide Punkte können jedoch der Beobachtung entgegenwirken, dass die definierende Deklaration der BMT in einer eigenen Quelldatei codiert werden könnte.Es könnte sich um eine Code-Review-Richtlinie für die Testsuite handeln, die die Testdata-Initialisierungenmuss Seien Sie so codiert - und vielleicht in Dateien, die sich an eine unverwechselbare Namenskonvention halten.Eine fanatische Richtlinie, aber nicht fanatischer als eine, die darauf besteht, dass alle Testdateninitialisierer aus Dateien extrahiert werden müssen.Wenn ein Betreuer verpflichtet ist, die BMT in jeder Datei zu befragen, wird dies keinen Unterschied machen, ob die Dateierweiterung ist .cpp, .dat oder Wasauchimmer:Alles, was zählt, ist die Verständlichkeit des „Codes“.

Für hartkodierende und gegen Extraktion aus einer Datendatei kann darauf hingewiesen werden, dass die Extraktion aus einer Datendatei eine Quelle irrelevanter potenzieller Fehler in Testfälle einführen muss - alle Sollte nicht passieren Fehler, die möglicherweise das Lesen der richtigen Daten aus einer Datei besiegen.Dies führt zu einem Overhead für die Testentwicklung, um eine korrekte und klare Unterscheidung zwischen echten Testfehlern und Fehlern zu bewirken, die Testdaten aus der Datei zu erwerben, und um alle möglichen Ursachen des letzteren eindeutig zu diagnostizieren.

Im Falle von Googletest und vergleichsweise funktionaler Frameworks kann dieser Punkt bis zu einem gewissen Grad entgegengewirkt werden, indem sie mit polymorphen Fixierbasisklassen wie angezeigt werden ::testing::Test Und ::testing::Environment.Diese erleichtern den Testentwickler bei der Einkapselung des Erwerbs von Testressourcen in der Initialisierung von Tests oder Test-SUIT, sodass alle erfolgreich oder mit einem diagnostizierten Fehler vorhanden sind, bevor die Bestandtests eines Testfalls durchgeführt werden.RAII kann eine problemlose Trennung zwischen Setup-Fehlern und echten Fehlern aufrechterhalten.

Trotzdem gibt es für die Datmodil-Route einen nicht reduzierbaren Aufwand für Dateihandlungen Und Es gibt einen operativen Overhead, den die RAII -Merkmale des Rahmens nichts reduzieren können.Im Umgang mit hohen Testsystemen, die mit Datendateien handeln, die Datendateien, die Datendateien handeln sind einfach Anfälliger für operative Pannen als Quelldateien, die nur zur Build-Zeit vorhanden und korrekt sein müssen.Datendateien werden während der Laufzeit mit größerer Wahrscheinlichkeit fehlt oder verlegt oder enthalten fehlerhaftes Zeug oder irgendwie geworden zu sein oder irgendwie bei der falschen Überarbeitung zu erscheinen.Ihre Verwendungen in einem Testsystem sind nicht so einfach oder starr gesteuert wie die von Quelldateien. Dinge, die nicht passieren sollten Bei Testdatendateien ist Teil der operativen Reibung von Testsystemen, die sich auf sie verlassen und proportional zu ihrer Anzahl sind.

Seit Quelldaten Kann die Initialisierungen von Testdaten hygienisch für Revisionsverfolgung einkapseln und sie können mit der Extraktion aus einer Datei die Extraktion ausgesetzt werden, wobei der Präprozessor das Extrahieren als Nebenprodukt der Kompilierung durchführt.Warum sollte man vor diesem Hintergrund andere Maschinen mit zusätzlichen Risiken einsetzen, um es zu extrahieren?Es kann gute Antworten geben, wie die vorgeschlagene XML-Schnittstelle mit Testmanagement und Defektmanagementsystemen, aber "Es sind Testdaten, also nicht Hardcode It" ist nicht gut.

Auch wenn eine Testsuite verschiedene Konfigurationen des zu testenden Systems unterstützen muss Build-Konfigurationen der Testsuite, Sie können es genauso gut (hygienisch) feststellen und die bedingte Kompilierung die richtige hartkodierende Auswahl auswählen lassen.

Bisher habe ich das Revision-Tracking-Hygeine-Argument für die data-basierte Trennung von Testdateninitialisierern nicht in Frage gestellt.Ich habe gerade darauf hingewiesen, dass reguläre Quelldateien, in denen die Initialisierer hart codiert sind, diese Segregation durchführen können.Und ich möchte dieses Argument nicht trellen, aber ich möchte die fanatische Schlussfolgerung, dass Testdateninitialisierer im Prinzip immer aus dedizierten Dateien extrahiert werden sollten - ob Quelldateien oder Datendateien, nicht mehr beenden.

Es besteht kein Grund, die Gründe für den Widerstand gegen diese Schlussfolgerung näher zu erläutern.Auf diese Weise liegt der Testcode, der lokal ist weniger Verständlich als der durchschnittliche Programmierer mit Pizza-Eating würde und Organisationen von Testsuite-Dateien, die weitaus schneller als notwendig oder gesund werden.Normativ sind alle primären Ressourcen der Testsuite "eine Art Code".Das Fähigkeit eines Programmierers umfasst die Fähigkeit, Code in Dateien mit angemessener Granularität zu verteilt, um den entsprechenden Überarbeitungs-Tracking-Hygeine zu sichern.Es handelt sich nicht um ein mechanisches Verfahren, sondern um eine Fachkompetenz, die von der Codeüberprüfung abgedeckt wird.Die Code-Überprüfung kann und sollte sicherstellen, dass die Initialisierungen der Testdaten, sie jedoch durchgeführt werden, gut gestaltet und verarbeitet werden, genau wie in allen anderen Routine-Respekten.

Endeffekt:Wenn Sie den gleichen Build Ihrer Testsuite für eine Vielzahl dieser Mock -Netzwerkantworten ausführen möchten, lesen Sie sie aus einer Datei aus.Wenn andererseits es invariant oder kovariante mit Build -Konfigurationen der Testsuite ist, ist es Warum nicht Hartes Code es?

Andere Tipps

(Vorbehalt: Diese Antwort gilt allgemein für jedes Unit-Test-Framework.)

Ich bevorzuge es, Testdatendateien als separate Objekte in einem Revisionskontrollsystem aufzubewahren.Dies bietet folgende Vorteile:

  • Sie könnten den Komponententest so programmieren, dass er eine oder mehrere Datendateien akzeptiert, um verschiedene Situationen zu testen
  • Sie können Änderungen in den Daten nach Bedarf verfolgen

Wenn Sie nicht möchten, dass die Unit-Test-Ausführung eine Datendatei liest, was in manchen Situationen eine notwendige Bedingung sein kann, können Sie ein Programm oder Skript schreiben, das C++-Code generiert, der den Vektor beim Einrichten des Fixtures initialisiert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top