Testy jednostkowe w Qt Creatorze

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

TemplateProj struktura.png

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

Dodaj komentarz

Twój adres email nie zostanie opublikowany.