W tym poście zostanie przedstawiona struktura projektu zawierającego aplikację oraz testy jednostkowe. Przedstawię dwa przykłady. Pierwszy z nich będzie oparty o wbudowaną bibliotekę QtTest, natomiast w drugim przykładzie zostanie wykorzystana biblioteka Catch, która jest lekka i składa się tylko z jednego pliku nagłówkowego.
Struktura projektu
Projekt (TemplateProj) składa się z dwóch podprojektów: standardowego projektu aplikacji (app) oraz projektu zawierającego testy jednostkowe (tests). Główny projekt (TemplateProj) to tak naprawdę tylko kontener na pozostałe projekty. Korzysta z szablonu TEMPLATE = subdirs i definiuje jedynie nazwy podkatalogów projektu. CONFIG += ordered informuje kompilator, że wymienione podkatalogi powinny być przetwarzane w takiej kolejności w jakiej są wymienione. Zapewnia to poprawną kompilację, ponieważ projekt tests korzysta z elementów projektu app. Zawartość pliku TemplateProj:
TEMPLATE = subdirs CONFIG += ordered SUBDIRS = \ app \ tests
Projekt app jest generalnie standardowym projektem aplikacji, takim jaki można stworzyć w Qt Creatorze z szablonu nowy projekt. Wpis CONFIG += console umożliwia prawidłowe działanie programu, gdy odpalamy program w terminalu zamiast domyślnego okna application output. Brak tego wpisu powoduje, że po odpaleniu aplikacji okno konsoli jest puste i wyświetla tylko standardowe „Press Return to close this window”. Zmiana opcji „Run in terminal” jest dostępna w karcie Projects → Run Settings osobno dla każdej aplikacji. DESTDIR = $$PWD/../build ustawia katalog z plikami wynikowymi na build w katalogu głównym projektu. Można tę linię pominąć, wtedy pliki wynikowe pojawią się w domyślnych katalogach debug / release.
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += console TARGET = TemplateProj TEMPLATE = app DESTDIR = $$PWD/../build HEADERS += \ Calculator.h SOURCES += \ main.cpp \ Calculator.cpp
W głównym projekcie app dodajmy plik main.cpp oraz przykładową klasę Calculator:
#include#include int main(int argc, char *argv[]) { std::cout << "Example test project" << std::endl; Calculator calc; std::cout << "2 + 6 = " << calc.add(2,6) << std::endl; return 0; }
#ifndef CALCULATOR_H #define CALCULATOR_H class Calculator { public: int add(int a, int b); }; #endif // CALCULATOR_H
#include "Calculator.h" int Calculator::add(int a, int b) { return a + b; }
Do tego miejsca wszystkie elementy są niezależne od użytej technologii testów. Natomiast projekt z testami jest już dostosowywany do danej biblioteki testowej.
QtTest
QtTest to biblioteka dostarczana wraz z całym pakietem Qt, więc stworzenie projektu zawierającego testy jednostkowe jest możliwe bez wprowadzania dodatkowych zależności od zewnętrznych bibliotek.
Najprostszy plik projektu tests dla QtTest wygląda następująco:
QT += testlib TARGET = TemplateProjUnitTests SOURCES += TestMain.cpp \ ../app/Calculator.cpp INCLUDEPATH += \ ../app
Plik TestMain.cpp (omówienie):
#include#include "Calculator.h" class TestMain: public QObject { Q_OBJECT private slots: void adder(); }; void TestMain::adder() { Calculator c; QCOMPARE(c.add(3,5), 8); QCOMPARE(c.add(3,10), 13); } QTEST_MAIN(TestMain) #include "TestMain.moc"
Link do kompletnego projektu: Qt-simple-examples/ProjectStructureQTest
Catch
Catch to biblioteka testów dla C++. Jej zaletami są liberalna licencja, brak zależności od innych bibliotek oraz dystrybucja w postaci jednego pliku nagłówkowego.
Najprostszy plik projektu tests dla Catch wygląda następująco (chociaż działa także bez linijki TEMPLATE = app, ale została ona pozostawiona dla formalności):
QT += core TARGET = TemplateProjUnitTests TEMPLATE = app CONFIG += console SOURCES += TestMain.cpp \ CalculatorTest.cpp \ ../app/Calculator.cpp INCLUDEPATH += \ ../app \ ../lib
Plik TestMain.cpp - odpowiada za prawidłowe wywołanie pozostałych plików źródłowych z testami. Zawiera w sobie definicje jednego makra i include biblioteki Catch. Makro CATCH_CONFIG_MAIN powinno zostać wywołane tylko w jednym miejscu projektu.
#define CATCH_CONFIG_MAIN #include "catch.hpp"
Właściwy plik z testami:
#include "catch.hpp" #include "Calculator.h" TEST_CASE("add test") { Calculator c; REQUIRE(c.add(3,5) == 8); REQUIRE(c.add(3,10) == 13); }
Link do kompletnego projektu: Qt-simple-examples/ProjectStructureCatch
O strukturze projektu: link
Przykład aplikacji wykorzystującej bibliotekę Catch: link
Potencjalne problemy
- qmake exited with code 2 - prawdopodobnie ścieżka projektu jest zbyt długa - wystarczy przenieść projekt do katalogu o krótszej ścieżce
- inne błędy przy kompilacji - kliknąć prawym przyciskiem na projekcie → Run qmake a następnie Rebuild