Din noiembrie 2007 am cerut ca toate noile caracteristici care intră în versiunea master să fie însoțite de teste de unitate. Inițial ne-am limitat la qgis_core, apoi vom extinde această cerință pentru alte părți ale bazei de cod, o dată ce dezvoltatorii se vor familiariza cu procedurile pentru testele de unitate, detaliate în secțiunile următoare.
Testele de unitate se efectuează folosind o combinație de QTestLib (biblioteca de testare Qt) și CTest (un cadru pentru compilarea și rularea testelor, ca parte a procesului de compilare CMake). Haideți să aruncăm o privire asupra procesului, înainte de a intra în detalii:
Să presupunem că există un cod pe care ar trebui să-l testați, cum ar fi o clasă sau o funcție. Adepții programării extreme susțin că imediat ce începeți scrierea unui cod ar trebui să începeți și construirea testelor, astfel că, pe măsură ce implementați codul, să puteți valida imediat, cu ajutorul testului, fiecare nouă parte funcțională adăugată. În practică va trebui, probabil, să scrieți teste pentru codul pre-existent în QGIS, deoarece efectuăm testarea mult prea târziu, după ce logica aplicației a fost deja implementată.
Creați un test de unitate. Acest lucru se întâmplă în directorul <QGIS Source Dir>/tests/src/core, în cazul bibliotecilor de bază. Testul este de fapt un client care creează o instanță a unei clase, apelând unele metode ale acestei clase. Acesta va verifica valorile returnate din fiecare metodă, pentru a se asigura că se potrivesc valorilor așteptate. În cazul în care unul dintre apeluri eșuează, testul de unitate va eșua.
Includeți macro-urile QtTestLib în clasa dvs. de test. Macrocomenzile sunt procesate de către compilatorul de obiecte meta Qt (MOC) și transformă clasa de testare într-o aplicație executabilă.
Adăugați o secțiune la fișierul CMakeLists.txt din directorul testelor dvs., care va construi testul.
Asigurați-vă că ați activat ENABLE_TESTING în ccmake / cmakesetup. Astfel, vă veți asigura că testele dumneavoastră se compilează atunci când tastați make.
Opțional, adăugați datele de testare în <QGIS Source Dir>/tests/testdata dacă testul dvs. este dirijat cu ajutorul datelor (de exemplu, trebuie încărcat un fișier shape). Aceste date de testare ar trebui să fie cât mai mici posibil, și ori de câte ori se poate, ar trebui să utilizați seturile de date existente deja acolo. Testele dvs. nu ar trebui să modifice aceste date în sine, ci mai degrabă o copie temporară, pe undeva, dacă este necesar.
Compilați sursele, apoi efectuați instalarea. Faceți acest lucru utilizând procedura normală make && (sudo) make install.
Rulați testele. Acest lucru se face în mod normal, pur și simplu prin accesarea make test după pasul make install, deși vom prezenta și alte abordări, care oferă un control mai fin asupra testelor de funcționare.
Având imaginea de ansamblu în minte, vom intra un pic în detalii. O mare parte din configurație este realizată deja în CMake, precum și în alte locuri din arborele sursei, astfel încât tot ce mai trebuie să faceți este simplu - să scrieți testele de unitate!
Crearea unui test de unitate este ușoară - de obicei, veți face acest lucru doar prin crearea un singur fișier .cpp (fișierul .h nu este utilizat), și implementând toate metodele de testare sub formă de metode publice care returnează valori vide. Pentru demonstrație, vom folosi o clasă de test simplă pentru QgsRasterLayer, de-a lungul secțiunii care urmează. Prin convenție vom numi testul nostru cu același nume ca și clasa de testare, dar prefixat cu ‘Test’. Așa că testul va fi stocat într-un fișier denumit testqgsrasterlayer.cpp iar clasa în sine va fi TestQgsRasterLayer. În primul rând vom adăuga informațiile privind drepturile de autor:
/***************************************************************************
testqgsvectorfilewriter.cpp
--------------------------------------
Date : Friday, Jan 27, 2015
Copyright: (C) 2015 by Tim Sutton
Email: [email protected]
***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
În continuare vom folosi declarațiile de includere, necesare testelor pe care ne propunem să le rulăm. Există una specială, care ar trebui să existe pentru toate testele:
#include <QtTest/QtTest>
Mai departe, continuați implementarea normală a clasei, incluzând antetele de care aveți nevoie:
//Qt includes...
#include <QObject>
#include <QString>
#include <QObject>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
//qgis includes...
#include <qgsrasterlayer.h>
#include <qgsrasterbandstats.h>
#include <qgsapplication.h>
Din moment ce combinăm atât declarația clasei cât și implementarea într-un singur fișier, urmează declarația clasei. Vom începe cu documentația doxygen. Fiecare caz de testare trebuie să fie documentat în mod corespunzător. Folosim directiva doxygen ingroup, astfel încât toate Unitățile de Testare apar ca module în documentația Doxygen generată. Urmează apoi o scurtă descriere a unității de testare, clasa trebuind să moștenească QObject și să includă macrocomanda Q_OBJECT.
/** \ingroup UnitTests
* This is a unit test for the QgsRasterLayer class.
*/
class TestQgsRasterLayer: public QObject
{
Q_OBJECT
Toate metodele noastre de testare sunt implementate ca sloturi private. Cadrul de lucru QtTest va apela succesiv fiecare metodă slot privată din clasa de test. Există patru metode ‘speciale’ care, dacă sunt implementate, vor fi apelate la începutul testului de unitate (initTestCase), și la sfârșitul testului unitate (cleanupTestCase). Înainte de apelarea fiecărei metode de testare va fi apelată metoda init(), iar după fiecare metodă de testare este apelată metoda cleanup(). Aceste metode sunt foarte utile întrucât vă permit alocarea și curățarea resurselor înainte de rularea fiecărui test, și a unității de testare în ansamblul său.
private slots:
// will be called before the first testfunction is executed.
void initTestCase();
// will be called after the last testfunction was executed.
void cleanupTestCase(){};
// will be called before each testfunction is executed.
void init(){};
// will be called after every testfunction.
void cleanup();
Apoi urmează metodele de testare, care nu ar trebui să preia parametri, dar ar trebui să se întoarcă valoarea vidă. Metodele vor fi apelate în ordinea declarației. Vom implementa două metode aici, care ilustrează două tipuri de teste. În primul caz, pentru a testa, în general, dacă diferitele părți ale clasei lucrează, vom folosi o abordare de testare funcțională. Încă o dată, programatorii extremi pledează pentru scrierea acestor teste înainte de implementarea clasei. Apoi, pe măsură ce lucrați la implementarea clasei dvs. rulați iterativ testele de unitate. Cât mai multe funcții de testare ar trebui să se finalizeze cu succes în timp ce implementarea clasei progresează, iar după ce întregul test de unitate se încheie corect, noua dvs. clasă este gata, dispunând de un mod repetabil de validare.
În mod normal, testele dumneavoastră de unitare vor acoperi doar API-ul public al clasei, nefiind necesară scrierea testelor pentru accesori şi mutatori. Dacă s-ar întâmpla ca un acccesor sau un mutator să nu funcționeze conform așteptărilor, veți implementa un test de regresie, pentru a verifica acest lucru (așa cum se poate vedea mai departe).
//
// Functional Testing
//
/** Check if a raster is valid. */
void isValid();
// more functional tests here ...
În continuare, vom implementa testele noastre de regresie. Implementarea acestora ar trebui să fie făcută în așa fel, încât să poată reproduce condițiile particulare ale oricărei erori. De exemplu, am primit recent un raport prin e-mail, în care se preciza că numărul de celule din rastere este întotdeauna mai mic cu 1, lucru care denatura toate statisticile pentru benzile raster. Am înregistrat eroarea (prin tichetul #832) și apoi am creat un test de regresie care replica defecțiunea, cu ajutorul unui set de date de test restrâns (un raster 10x10). Apoi am rulat testul, certificând veridicitatea informației (faptul că numărul de celule a fost de 99 în loc de 100). Am corectat eroarea, am rulat iarăși testul de unitate și trecând cu succes de testul de regresie. Am urcat în baza de cod testul de regresie, alături de codul corect. Ulterior, dacă cineva se va bloca la acest pas, vom putea identifica imediat porțiunea de cod care a regresat. În plus, pe viitor, chiar înainte de a înregistra modificările în baza codului principal, rulând aceste teste, ne vom asigura că schimbările aduse nu au efecte adverse neașteptate - cum ar fi întreruperea funcționalității existente.
Mai există un beneficiu al prezenței testelor de regresie - acestea pot aduce economie de timp. Dacă ați depanat vreodată o eroare care a implicat efectuarea de modificări în codul sursă, urmată de rularea aplicației și efectuarea unei serii de pași extrem de complicați pentru a reproduce problema, va fi imediat evident că simpla implementare a testului de regresie înainte de corectarea erorii permite automatizarea testării, ceea ce reprezintă o eliminare eficientă a defectelor.
Pentru a implementa testul de regresie, ar trebui să urmați convenția de denumire a regresiei<TicketID> pentru funcțiile de testare. În cazul în care nu există deja un tichet Redmine pentru regresie, ar trebui să creați mai întâi unul. Această abordare facilitează persoanei care rulează un test de regresie nereușit, obținerea de informații suplimentare.
//
// Regression Testing
//
/** This is our second test case...to check if a raster
* reports its dimensions properly. It is a regression test
* for ticket #832 which was fixed with change r7650.
*/
void regression832();
// more regression tests go here ...
În cele din urmă, în declarația clasei test puteți declara, în mod privat, datele membru și metodele ajutătoare de care ați putea avea nevoie în testele de unitate. În cazul nostru, vom declara un QgsRasterLayer * care poate fi folosit de către oricare dintre metodele noastre de testare. Stratul raster va fi creat în funcția initTestCase(), care este rulat înainte de oricare alte teste, iar apoi va fi distrus de către cleanupTestCase(), care se execută după toate testele. Prin declararea metodelor ajutătoare (care pot fi apelate prin diverse funcții de testare) în mod privat, vă veți asigura că nu vor fi apelate în mod automat de către executabilul QTest, care este creat în urma compilării testului nostru.
private:
// Here we have any data structures that may need to
// be used in many test cases.
QgsRasterLayer * mpLayer;
};
Astfel se termină declarația clasei noastre. Implementarea are loc pur și simplu, în partea de jos a fișierului. Mai întâi are loc inițializarea și curățarea funcțiilor:
void TestQgsRasterLayer::initTestCase()
{
// init QGIS's paths - true means that all path will be inited from prefix
QString qgisPath = QCoreApplication::applicationDirPath ();
QgsApplication::setPrefixPath(qgisPath, TRUE);
#ifdef Q_OS_LINUX
QgsApplication::setPkgDataPath(qgisPath + "/../share/qgis");
#endif
//create some objects that will be used in all tests...
std::cout << "PrefixPATH: " << QgsApplication::prefixPath().toLocal8Bit().data() << std::endl;
std::cout << "PluginPATH: " << QgsApplication::pluginPath().toLocal8Bit().data() << std::endl;
std::cout << "PkgData PATH: " << QgsApplication::pkgDataPath().toLocal8Bit().data() << std::endl;
std::cout << "User DB PATH: " << QgsApplication::qgisUserDbFilePath().toLocal8Bit().data() << std::endl;
//create a raster layer that will be used in all tests...
QString myFileName (TEST_DATA_DIR); //defined in CmakeLists.txt
myFileName = myFileName + QDir::separator() + "tenbytenraster.asc";
QFileInfo myRasterFileInfo ( myFileName );
mpLayer = new QgsRasterLayer ( myRasterFileInfo.filePath(),
myRasterFileInfo.completeBaseName() );
}
void TestQgsRasterLayer::cleanupTestCase()
{
delete mpLayer;
}
Funcția de inițializare de mai sus ilustrează câteva lucruri interesante.
Trebuie să setăm manual calea către datele aplicației QGIS, astfel încât resursele, cum ar fi srs.db, să poată fi găsite în mod corect.
În al doilea rând, acesta este un test dirijat cu ajutorul datelor, astfel încât trebuie să oferim o modalitate de a localiza, în mod generic, fișierul tenbytenraster.asc. Acest lucru a fost realizat prin utilizarea definiția de compilator TEST_DATA_PATH. Definiția este creată în fișierul de configurare CMakeLists.txt din <QGIS Source Root>/tests/CMakeLists.txt, fiind disponibilă pentru toate testele de unitate QGIS. Dacă aveți nevoie de date pentru testul dumneavoastră, urcați-le în <QGIS Source Root>/tests/testdata. Ar trebui să urcați aici doar seturi de date foarte mici. În cazul în care testul trebuie să modifice datele, ar trebui să faceți mai întâi o copie a acestora.
Qt oferă, de asemenea, alte câteva mecanisme interesante pentru testarea dirijată cu ajutorul datelor, așa că, dacă vă interesează să aflați mai multe despre acest subiect, consultați documentația Qt.
În continuare, haideți să ne uităm la testul nostru funcțional. Testul isValid() verifică dacă stratul raster a fost încărcat corect în initTestCase. QVERIFY este o macrocomandă Qt pe care o puteți utiliza pentru a evalua o condiție de testare. Qt prevede alte câteva macro-uri care se pot utiliza în testele dumneavoastră, printre care:
Unele dintre aceste macro-uri sunt utile numai atunci când se utilizează cadrul de lucru Qt, pentru testarea cu ajutorul datelor (a se vedea documentația Qt, pentru mai multe detalii).
void TestQgsRasterLayer::isValid()
{
QVERIFY ( mpLayer->isValid() );
}
În mod normal, testele funcționale ar putea acoperi toată gama de funcționalități a claselor dvs. API publice, acolo unde este fezabil. Testele funcționale fiind gata, ne putem uita la exemplul nostru de test de regresie.
Având în vedere că problema #832 raportează un număr eronat de celule, scrierea testului nostru constă pur și simplu în folosirea macrocomenzii QVERIFY, pentru a verifica dacă numărul de celule corespunde valorii așteptate:
void TestQgsRasterLayer::regression832()
{
QVERIFY ( mpLayer->getRasterXDim() == 10 );
QVERIFY ( mpLayer->getRasterYDim() == 10 );
// regression check for ticket #832
// note getRasterBandStats call is base 1
QVERIFY ( mpLayer->getRasterBandStats(1).elementCountInt == 100 );
}
Având toate funcțiile de testare a unităților implementate, rămâne să adăugăm un ultim lucru clasei noastre de testare:
QTEST_MAIN(TestQgsRasterLayer)
#include "testqgsrasterlayer.moc"
Scopul acestor două linii este de a semnala compilatorului moc Qt că are de-a face cu un QtTest (acesta va genera o metodă principală, care, la rândul ei, va apela fiecare test funcțional. Ultima linie reprezintă definiția de includere pentru sursele generate de MOC. Ar trebui să înlocuiți ‘testqgsrasterlayer’ cu numele, scris cu litere mici, al clasei dvs.
Adăugarea testului de unitate în sistemul de compilare reprezintă doar o chestiune de editare a fișierului CMakeLists.txt din directorul de testare, prin clonarea unuia dintre blocurile de testare existente, și apoi prin înlocuirea numele clasei de test din interiorul său. De exemplu:
# QgsRasterLayer test
ADD_QGIS_TEST(rasterlayertest testqgsrasterlayer.cpp)
Vom parcurge pe scurt aceste linii, pentru a explica ceea ce fac, dar dacă nu vă interesează, trebuie să faceți doar pasul explicat în secțiunea de mai sus.
MACRO (ADD_QGIS_TEST testname testsrc)
SET(qgis_${testname}_SRCS ${testsrc} ${util_SRCS})
SET(qgis_${testname}_MOC_CPPS ${testsrc})
QT4_WRAP_CPP(qgis_${testname}_MOC_SRCS ${qgis_${testname}_MOC_CPPS})
ADD_CUSTOM_TARGET(qgis_${testname}moc ALL DEPENDS ${qgis_${testname}_MOC_SRCS})
ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS})
ADD_DEPENDENCIES(qgis_${testname} qgis_${testname}moc)
TARGET_LINK_LIBRARIES(qgis_${testname} ${QT_LIBRARIES} qgis_core)
SET_TARGET_PROPERTIES(qgis_${testname}
PROPERTIES
# skip the full RPATH for the build tree
SKIP_BUILD_RPATHTRUE
# when building, use the install RPATH already
# (so it doesn't need to relink when installing)
BUILD_WITH_INSTALL_RPATH TRUE
# the RPATH to be used when installing
INSTALL_RPATH ${QGIS_LIB_DIR}
# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
INSTALL_RPATH_USE_LINK_PATH true)
IF (APPLE)
# For Mac OS X, the executable must be at the root of the bundle's executable folder
INSTALL(TARGETS qgis_${testname} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
ADD_TEST(qgis_${testname} ${CMAKE_INSTALL_PREFIX}/qgis_${testname})
ELSE (APPLE)
INSTALL(TARGETS qgis_${testname} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
ADD_TEST(qgis_${testname} ${CMAKE_INSTALL_PREFIX}/bin/qgis_${testname})
ENDIF (APPLE)
ENDMACRO (ADD_QGIS_TEST)
Să ne uităm un pic, mai în detaliu, la liniile individuale. Mai întâi definim lista surselor pentru testul nostru. Din moment ce avem doar un singur fișier sursă (urmând metodologia I descrisă mai sus, în care declarația și definiția de clasă rezidă în același fișier) vom efectua o declarație simplă:
SET(qgis_${testname}_SRCS ${testsrc} ${util_SRCS})
Din moment ce clasa noastră de test trebuie să fie executată prin intermediul compilatorului meta-obiect (MOC) din Qt, trebuie să indicăm acest lucru printr-o pereche de linii:
SET(qgis_${testname}_MOC_CPPS ${testsrc})
QT4_WRAP_CPP(qgis_${testname}_MOC_SRCS ${qgis_${testname}_MOC_CPPS})
ADD_CUSTOM_TARGET(qgis_${testname}moc ALL DEPENDS ${qgis_${testname}_MOC_SRCS})
În continuare vom indica lui cmake faptul că trebuie creeze un executabil din clasa de test. Am efectuat acest lucru în secțiunea anterioară, pe ultima linie a implementării clasei, unde am inclus ieșirile MOC direct în clasa noastră de testare, astfel că îi vom oferi (printre altele), o metodă principală, astfel încât clasa să poate fi compilată ca un executabil:
ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS})
ADD_DEPENDENCIES(qgis_${testname} qgis_${testname}moc)
În continuare, trebuie să specificăm orice dependențe față de anumite biblioteci. Momentan, clasele au fost implementate cu o dependență catch-all QT_LIBRARIES, dar vom înlocui această clauză doar cu acele biblioteci Qt de care are nevoie fiecare clasă. Desigur, de asemenea, trebuie să conectați și bibliotecile QGIS relevante, în conformitate cu cerințele testului de unitate.
TARGET_LINK_LIBRARIES(qgis_${testname} ${QT_LIBRARIES} qgis_core)
În continuare vom spune lui cmake să instaleze testele în același loc cu cel al binarelor QGIS. Acest lucru îl vom elimina pe viitor, astfel încât testele să poată rula direct din interiorul arborelui sursă.
SET_TARGET_PROPERTIES(qgis_${testname}
PROPERTIES
# skip the full RPATH for the build tree
SKIP_BUILD_RPATHTRUE
# when building, use the install RPATH already
# (so it doesn't need to relink when installing)
BUILD_WITH_INSTALL_RPATH TRUE
# the RPATH to be used when installing
INSTALL_RPATH ${QGIS_LIB_DIR}
# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
INSTALL_RPATH_USE_LINK_PATH true)
IF (APPLE)
# For Mac OS X, the executable must be at the root of the bundle's executable folder
INSTALL(TARGETS qgis_${testname} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
ADD_TEST(qgis_${testname} ${CMAKE_INSTALL_PREFIX}/qgis_${testname})
ELSE (APPLE)
INSTALL(TARGETS qgis_${testname} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
ADD_TEST(qgis_${testname} ${CMAKE_INSTALL_PREFIX}/bin/qgis_${testname})
ENDIF (APPLE)
În cele din urmă s-a folosit ADD_TEST pentru a înregistra testul cu cmake / ctest. Aici este locul în care se întâmplă magia - înregistrarea clasei cu ctest. Dacă vă amintiți, în prezentarea generală de la începutul acestei secțiuni, folosim QtTest și CTest împreună. Recapitulând, QtTest adaugă o metodă principală unității de testare și gestionează apelurile metodelor de testare din cadrul clasei. De asemenea, furnizează unele comenzi macro, cum ar fi QVERIFY, pe care le putem utiliza pentru rularea testelor folosind condiții. Rezultatul unității de testare QtTest este un executabil pe care îl putem rula din linia de comandă. Cu toate acestea, atunci când avem o suită de teste și dorim să rulăm individual fiecare executabil, și în plus, să integrăm testele de funcționare în procesul de compilare, folosim CTest.
Pentru a compila testul de unitate rewbuie doar să vă asigurați că ENABLE_TESTS = true în configurația cmake. Există două moduri de a face acest lucru:
Rulați ccmake .. ( sau cmakesetup .. sub windows) și setați, în mod interactiv, fanionul ENABLE_TESTS pe ON.
Adăugați o linie de comandă pentru fanion în cmake ex.: cmake -DENABLE_TESTS=true ..
La urmă, doar compilați normal QGIS, iar testele ar trebui să se compileze, la rândul lor.
Cel mai simplu mod de a rula testele este ca parte a procesului normal de compilare:
make && make install && make test
Comanda de testare va invoca CTest, care va rula fiecare test care a fost înregistrat cu directiva ADD_TEST CMake, descrisă mai sus. Rezultatul tipic al testului va arăta astfel:
Running tests...
Start processing tests
Test project /Users/tim/dev/cpp/qgis/build
## 13 Testing qgis_applicationtest***Exception: Other
## 23 Testing qgis_filewritertest *** Passed
## 33 Testing qgis_rasterlayertest*** Passed
## 0 tests passed, 3 tests failed out of 3
The following tests FAILED:
## 1- qgis_applicationtest (OTHER_FAULT)
Errors while running CTest
make: *** [test] Error 8
Dacă un test eșuează, puteți utiliza comanda ctest pentru a-i examina mai îndeaproape cauza. Utilizați opțiunea -R pentru a specifica un regex pentru testele pe care doriți să le rulați, și un `` -V`` pentru a obține o ieșire detaliată:
$ ctest -R appl -V
Start processing tests
Test project /Users/tim/dev/cpp/qgis/build
Constructing a list of tests
Done constructing a list of tests
Changing directory into /Users/tim/dev/cpp/qgis/build/tests/src/core
## 13 Testing qgis_applicationtest
Test command: /Users/tim/dev/cpp/qgis/build/tests/src/core/qgis_applicationtest
********* Start testing of TestQgsApplication *********
Config: Using QTest library 4.3.0, Qt 4.3.0
PASS : TestQgsApplication::initTestCase()
PrefixPATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/../
PluginPATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//lib/qgis
PkgData PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//share/qgis
User DB PATH: /Users/tim/.qgis/qgis.db
PASS : TestQgsApplication::getPaths()
PrefixPATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/../
PluginPATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//lib/qgis
PkgData PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//share/qgis
User DB PATH: /Users/tim/.qgis/qgis.db
QDEBUG : TestQgsApplication::checkTheme() Checking if a theme icon exists:
QDEBUG : TestQgsApplication::checkTheme()
/Users/tim/dev/cpp/qgis/build/tests/src/core/..//share/qgis/themes/default//mIconProjectionDisabled.png
FAIL!: TestQgsApplication::checkTheme() '!myPixmap.isNull()' returned FALSE. ()
Loc: [/Users/tim/dev/cpp/qgis/tests/src/core/testqgsapplication.cpp(59)]
PASS : TestQgsApplication::cleanupTestCase()
Totals: 3 passed, 1 failed, 0 skipped
********* Finished testing of TestQgsApplication *********
-- Process completed
***Failed
## 0 tests passed, 1 tests failed out of 1
The following tests FAILED:
## 1- qgis_applicationtest (Failed)
Errors while running CTest
Well that concludes this section on writing unit tests in QGIS. We hope you will get into the habit of writing test to test new functionality and to check for regressions. Some aspects of the test system (in particular the CMakeLists.txt parts) are still being worked on so that the testing framework works in a truly platform way. I will update this document as things progress.