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