Kapitel 45. Durchführung von Tests

Dieses Kapitel enthüllt eine unerwartete Denkweise: wie Sie aufhören, Angst vor Codeänderungen zu haben, und beginnen, diese selbstbewusst vorzunehmen. Sie entdecken, warum professionelle Entwickler weniger Zeit mit Debugging verbringen, erfahren das Geheimnis der schnellen Regressionsidentifizierung und erkennen, wie Tests zu Ihrem Schutzmechanismus bei jeder Änderung werden.

Es geht um Unit-Tests, das QtTest-Modul, Überprüfungen mit QCOMPARE und QVERIFY sowie um datengesteuerte Tests, die es ermöglichen, Code-Duplizierung drastisch zu reduzieren und die Zuverlässigkeit kritischer Bereiche zu erhöhen.

Dieses Kapitel zu überspringen ist das Risiko, zu mehrstündigem Debugging zurückzukehren. Es zu lesen ist die Chance, den Entwicklungsstil für immer zu ändern.

Das Kapitel enthält Code-Beispiele, die sofort einsatzbereit sind.

Selbstüberprüfung zum Kapitel

Was ist der wesentliche Unterschied zwischen Unit-Tests und System-Tests?Antwort
Richtige Antwort: Unit-Tests prüfen jede Klasse isoliert, unter der Annahme, dass alles andere korrekt funktioniert. System-Tests prüfen die gesamte Anwendung mit der Interaktion von GUI und allen Komponenten.
Warum wird empfohlen, Tests nach jeder Kompilierung auszuführen und nicht periodisch?Antwort
Richtige Antwort: Häufiges Ausführen von Tests ermöglicht es, Fehler sofort zu lokalisieren, da Sie genau wissen, was sich seit der letzten Kompilierung geändert hat. Dies verkürzt die Debugging-Zeit drastisch und verhindert die Anhäufung von Fehlern.
Warum sollte man Tests vor dem Schreiben der Klassenimplementierung erstellen?Antwort
Richtige Antwort: Das Schreiben eines Tests zwingt dazu, die Aufgabe besser zu durchdenken und die Frage zu stellen „Was muss getan werden, um die Implementierung hinzuzufügen”. Dies verbessert das Design und deckt Probleme in einem frühen Stadium auf.
Warum sollte man nicht versuchen, Tests für alle Klassen und alle möglichen Fälle zu schreiben?Antwort
Richtige Antwort: Übermäßiger Eifer kann das Gefühl erzeugen, dass das Testen zu viel Zeit in Anspruch nimmt, und Sie werden sie ganz aufgeben. Es ist besser, Tests für verdächtige kritische Stellen und Grenzfälle zu schreiben.
Warum sollte beim ersten Testlauf der Code absichtlich so modifiziert werden, dass der Test fehlschlägt?Antwort
Richtige Antwort: Dies prüft, ob der Test tatsächlich ausgeführt wird und in der Lage ist, Fehler zu erkennen. Wenn der Test selbst mit absichtlich falschem Code besteht, funktioniert der Test nicht wie beabsichtigt.
Was passiert, wenn im Makro QFETCH() ein Variablenname angegeben wird, der nicht mit dem Spaltennamen in der Datentabelle übereinstimmt?Antwort
Richtige Antwort: Der Test endet mit einer Fehlermeldung, da das Datenelement mit dem angegebenen Namen in der Testdatentabelle nicht gefunden wird.
Was ist der Vorteil von datengesteuerten Tests gegenüber dem direkten Einfügen von Daten in QCOMPARE()?Antwort
Richtige Antwort: Die Trennung von Testcode und Daten minimiert die Duplizierung. Neue Testfälle lassen sich einfach zum _data()-Slot hinzufügen, ohne den Test selbst zu modifizieren.
Warum muss die Testklasse von QObject erben und das Makro Q_OBJECT enthalten?Antwort
Richtige Antwort: Dies ist erforderlich, um Meta-Informationen zu erstellen, die es ermöglichen, Klassen-Slots während der Ausführung aufzurufen, einschließlich Test-Slots über den Qt-Introspektionsmechanismus.
In welchem Fall sollte QVERIFY() anstelle von QCOMPARE() verwendet werden?Antwort
Richtige Antwort: QVERIFY() wird verwendet, wenn die Wahrheit einer Bedingung oder eines booleschen Ausdrucks überprüft werden muss (z.B. isModified()), während QCOMPARE() zum Vergleich zweier konkreter Werte dient.
Welche QTest-Methode sollte verwendet werden, um die Eingabe einer Textzeichenfolge in ein Widget zu simulieren, und warum sollte nicht jeder Tastendruck einzeln simuliert werden?Antwort
Richtige Antwort: Verwenden Sie QTest::keyClicks() zur Eingabe der gesamten Zeichenfolge. Dies ist einfacher und schneller als mehrfache Aufrufe von keyClick() für jedes Zeichen, obwohl letzteres mehr Kontrolle bietet.
Was passiert mit der Testausführung, wenn die Behebung eines Fehlers einen neuen Fehler an anderer Stelle im Code erzeugt?Antwort
Richtige Antwort: Bei häufigem Ausführen von Tests wird der neue Fehler sofort nach der Kompilierung erkannt, da Tests die gesamte Funktionalität prüfen. Ohne Tests kann eine solche Regression lange unbemerkt bleiben.
Wozu dienen die Methoden initTestCase() und cleanupTestCase(), und wann werden sie aufgerufen?Antwort
Richtige Antwort: Sie werden am Anfang und am Ende der Ausführung aller Tests aufgerufen (nicht jedes einzelnen Tests) und dienen zur Initialisierung gemeinsamer Ressourcen und zur endgültigen Bereinigung.
Wie verwendet man den Parameter -eventdelay zur Fehlersuche in der GUI, und worin liegt sein Nutzen?Antwort
Richtige Antwort: Der Parameter zwingt den Test, zwischen Ereignissen Pausen zu machen, sodass man visuell beobachten kann, was in der Benutzeroberfläche geschieht. Dies hilft, Fehler im Zusammenhang mit asynchronen Aktualisierungen und dem Neuzeichnen von Widgets zu finden.

Praktische Aufgaben

Einfaches Niveau

Test mathematischer Operationen mit Grenzwerten
Erstellen Sie eine Klasse Calculator mit den Methoden add(), subtract(), multiply() und divide(). Schreiben Sie einen Unit-Test Test_Calculator mit Überprüfung von Grenzfällen: Division durch Null, Arbeit mit negativen Zahlen, Überlauf bei großen Zahlen, Operationen mit Null. Verwenden Sie den datengesteuerten Ansatz (Data-Driven Testing) für jede Methode.
Hinweise: Erstellen Sie Slots add_data(), subtract_data() usw. Sehen Sie in der Datentabelle Zeilen für Grenzfälle vor: (0, 0), (INT_MAX, 1), (-5, -3), (10, 0) für divide(). Für Division durch Null können Sie überprüfen, dass das Ergebnis einem speziellen Wert entspricht oder eine Ausnahme geworfen wird. Vergessen Sie nicht das Q_OBJECT-Makro und die Einbindung von test.moc.

Mittleres Niveau

Testen eines benutzerdefinierten Widgets mit Ereignissen
Erstellen Sie ein Widget CustomCounter basierend auf QPushButton mit einem internen Zähler, der bei jedem Klick erhöht wird und den Schaltflächentext auf „Clicked: N mal” ändert. Schreiben Sie einen Test, der 5 Mausklicks simuliert, die Korrektheit des Schaltflächentexts nach jedem Klick prüft, QTestEventList zur Aufzeichnung einer Ereignissequenz verwendet und überprüft, dass das Signal clicked() die richtige Anzahl von Malen mit Hilfe von QSignalSpy ausgegeben wird.
Hinweise: Verwenden Sie in der Testklasse QTest::mouseClick() zur Simulation. QSignalSpy spy(&button, &QPushButton::clicked) hilft beim Zählen von Signalen – prüfen Sie spy.count(). QTestEventList ermöglicht es, eine Reihe von Aktionen über addMouseClick() aufzuzeichnen und über simulate() wiederzugeben. Fügen Sie kleine Verzögerungen zwischen Klicks über addDelay() für einen realistischeren Test hinzu.

Schwieriges Niveau

Umfassendes Testen eines Dateneingabeformulars
Erstellen Sie ein LoginForm-Formular mit QLineEdit für Anmeldename und Passwort, QCheckBox „An mich erinnern” und QPushButton „Anmelden”. Die Schaltfläche sollte inaktiv sein, bis beide Felder ausgefüllt sind. Schreiben Sie ein vollständiges Testmodul, das: 1) den Anfangszustand des Formulars prüft, 2) Dateneingabe über die Tastatur simuliert, 3) die Aktivierung der Schaltfläche überprüft, 4) die Validierung testet (Anmeldename mindestens 3 Zeichen, Passwort 6 Zeichen), 5) einen Klick auf die Schaltfläche simuliert und die Ausgabe des Signals loginAttempted(QString, QString, bool) überprüft, 6) Startparameter zur Ausgabe eines XML-Berichts verwendet. Erstellen Sie auch eine CMakeLists.txt zum Erstellen des Tests.
Hinweise: Teilen Sie das Testen in Methoden auf: testInitialState(), testDataInput(), testValidation(), testLoginAttempt(). Verwenden Sie QSignalSpy zur Überprüfung von Signalen. Zur Überprüfung der Schaltflächenaktivität verwenden Sie QVERIFY(button.isEnabled()). Binden Sie in CMakeLists.txt Qt6::Test und Qt6::Widgets ein, verwenden Sie qt_add_test(). Führen Sie den Test mit dem Parameter -o results.xml,xml aus, um einen XML-Bericht zu erhalten. Vergessen Sie nicht die Methoden initTestCase() zum Erstellen des Formulars und cleanupTestCase() zur Bereinigung.

💬 Beteiligen Sie sich an der Diskussion!

Unit-Testing in Qt gemeistert? Haben Sie Fragen dazu, wie Sie Tests für komplexe Klassen richtig organisieren oder wie Sie komplexe Benutzerszenarien simulieren können?

Teilen Sie Ihre Erfahrungen mit der Testautomatisierung, diskutieren Sie komplexe Fälle mit dem datengesteuerten Ansatz und helfen Sie anderen Lesern, eine Testkultur in ihren Qt-Projekten zu etablieren!

Leave a Reply

Your email address will not be published. Required fields are marked *