Development/Tutorials/Unittests (pt BR)

< Development‎ | Tutorials
Revision as of 20:46, 29 June 2011 by Neverendingo (Talk | contribs) (Text replace - "<code>" to "<syntaxhighlight lang="text">")

Jump to: navigation, search


Development/Tutorials/Unittests


Author: Brad Hards, Sigma Bravo Pty Limited

Abstract

Esse artigo é um guia para escrever testes unitários para Qt4 e KDE4, baseado no framework QtTestLib lançado no Qt 4.1. Esse provê uma introdução para as idéias por trás de testes unitários, tutorial sobre framework QtTestLib e sugestões para obter um resultado melhor nos testes. Esse documento está de acordo com o Qt4.1 e KDE4.

Sobre Testes Unitários

Testes unitários verificam a funcionalidade, comportamento e conformidade de um componente do software. No Qt4 (incluindo KDE4) testes unitários são quase sempre usados para testar uma única classe C++ (embora testar uma macro ou função C é possível também).

Testes unitários são a parte principal do TDD (Desenvolvimento Orientado/Dirigido a Testes), de qualquer forma eles são úteis para todos os processos de desenvolvimento de software. Não é necessário que todo o código tenha cobertura de testes unitários (embora isso seja desejável!). Mesmo um único teste é passo útil para aumentar a qualidade do código.

Note que testes unitários são testes dinâmicos (eles rodam usando o código compilado) melhor que testes de análise estática (que age no código fonte ou em uma representação intermediária disso).

Mesmo se não são chamados "testes unitários", a maioria dos programadores tem escrito algum código "descartável" que eles usam para verificar uma implementação. Se esse código foi um pouco limpo, e desenvolvido em um sistema de desenvolvimento, então poderia ser usado várias vezes para verificar se a implementação ainda está funcionando. Para fazer isso um pouco mais fácil, nós podemos usar frameworks de teste.

Note que algumas vezes isso é uma tentação para tratar o teste unitário puramente como uma ferramenta de verificação. Enquanto é verdade que testes unitários ajudam e muito a garantir correta funcionalidade e comportamento, eles também contribuem com outros aspectos da qualidade do código, e pensando sobre que inputs precisam ser testados podem ajudar a identificar problemas de lógica no código (mesmo antes de rodar os testes). Em adição, o necessário para fazer o código estável é guia muito útil para garantir que classes não sofram de close coupling.

Depois de muitos conceitos - vamos falar sobre uma ferramenta específica que ode reduzir algum esforços e deixar nós chegarmos no trabalho propriamente dito.

Sobre QtTestLib

QtTestLib é uma biblioteca de testes leve desenvolvida pela TrollTech e lançada sobre a lincença GPL (uma versão comercial está disponível também, mas é necessário uma licença diferente). É escrito em C++ e é multiplataforma. Faz parte do Qt 4.1 e antes disso, foram lançadas versão separadamente. Versões antigas tem uma API diferente e os exemplos não vão funcionar com versões antigas.

Em adição a capacidade dos testes unitários, QtTestLib também oferece testes básicos de interface, baseados em QEvents. Isso te permite testar GUI widgets, mas não é geralmente indicado para testar uma aplicação inteira.

Cada testcase é um aplicção de teste independente. Diferente de CppUnit ou JUnit, não há uma classe tipo Runner. Ao invés, cada testcase é um executável que é só rodar.

Tutorial 1: Um simples teste de uma classe Data

Neste tutorial, nós vamos construir um teste simples para uma classe que representa uma data, usando QtTestLib como framework de teste. Para evitar muitos detalhes de como a classe data funciona, nós vamos usar apenas QDate. Classe que vem com o Qt. Em um teste unitário normal, você provavelmente testaria código escrito por você mesmo.

O código abaixo é um testecase completo.

Example 1. QDate test code

   1 #include <QtTest>
   2 #include <QtCore>
   3 
   4 class testDate: public QObject
   5 {
   6     Q_OBJECT
   7 private slots:
   8     void testValidity();
   9     void testMonth();
  10 };
  11 
  12 void testDate::testValidity()
  13 {
  14     // 11 March 1967
  15     QDate date( 1967, 3, 11 );
  16     QVERIFY( date.isValid() );
  17 }
  18 
  19 void testDate::testMonth()
  20 {
  21     // 11 March 1967
  22     QDate date;
  23     date.setYMD( 1967, 3, 11 );
  24     QCOMPARE( date.month(), 3 );
  25     QCOMPARE( QDate::longMonthName(date.month()),
  26               QString("March") );
  27 }
  28 
  29 
  30 QTEST_MAIN(testDate)
  31 #include "tutorial1.moc"
  32 </code>
  33 Save as tutorial1.cpp
  34 
  35 Stepping through the code, the first line imports the header files for the QtTest namespace. The second line imports the headers for the QtCore namespace (not strictly necessary, since QtTest also imports it, but it is robust and safe). Lines 4 to 10 give us the test class, testData. Note that testDate inherits from QObject and has the Q_OBJECT macro - QtTestLib requires specific Qt functionality that is present in  QObject. 
  36 
  37 Lines 12 to 17 provide our first test, which checks that a date is valid. Note the use of the QVERIFY macro, which checks that the condition is true. So if date.isValid() returns true, then the test will pass, otherwise the test will fail. QVERIFY is similar to ASSERT in other test suites.
  38 
  39 Similarly, lines 19 to 27 provide another test, which checks a setter, and a couple of accessor routines. In this case, we are using QCOMPARE, which checks that the conditions are equal. So if date.month() returns 3, then that part of that test will pass, otherwise the test will fail. 
  40 
  41 {{Warning|As soon as a QVERIFY evaluates to false or a QCOMPARE does not have two equal values, the whole test is marked as failed and the next test will be started. So in the example above, if the check at line 24 fails, then the check at lines 25 and 26 will not be run.}}
  42 
  43 In a later tutorial we will see how to work around problems that this behaviour can cause.
  44 
  45 Line 30 uses the QTEST_MAIN which creates an entry point routine for us, with appropriate calls to invoke the testDate unit test class. 
  46 
  47 Line 31 includes the Meta-Object compiler output, so we can make use of our  QObject functionality. 
  48 
  49 The qmake project file that corresponds to that code is shown below. You would then use qmake to turn this into a Makefile and then compile it with make.
  50 
  51 '''Example 2. QDate unit test project'''
  52 <syntaxhighlight lang="text">
  53 CONFIG += qtestlib
  54 TEMPLATE = app
  55 TARGET += 
  56 DEPENDPATH += .
  57 INCLUDEPATH += .
  58 
  59 # Input
  60 SOURCES += tutorial1.cpp
  61 </code>
  62 Save as tutorial1.pro
  63 
  64 This is a fairly normal project file, except for the addition of the <syntaxhighlight lang="text">CONFIG += qtestlib</code>. This adds the right header and library setup to the Makefile. 
  65 
  66 Create an empty file called tutorial1.h and compile with <syntaxhighlight lang="text">qmake; make</code> The output looks like the following:
  67 
  68 '''Example 3. QDate unit test output'''
  69 <syntaxhighlight lang="text">
  70 $ ./tutorial1
  71 ********* Start testing of testDate *********
  72 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051003
  73 PASS   : testDate::initTestCase()
  74 PASS   : testDate::testValidity()
  75 PASS   : testDate::testMonth()
  76 PASS   : testDate::cleanupTestCase()
  77 Totals: 4 passed, 0 failed, 0 skipped
  78 ********* Finished testing of testDate *********
  79 </code>
  80 
  81 Looking at the output above, you can see that the output includes the version of the test library and Qt itself, and then the status of each test that is run. In addition to the testValidity and testMonth tests that we defined, there is also a setup routine (initTestCase) and a teardown routine (cleanupTestCase) that can be used to do additional configuration if required.
  82 
  83 ===Failing tests===
  84 
  85 If we had made an error in either the production code or the unit test code, then the results would show an error. An example is shown below:
  86 
  87 '''Example 4. QDate unit test output showing failure'''
  88 <syntaxhighlight lang="text">
  89 $ ./tutorial1
  90 ********* Start testing of testDate *********
  91 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051003
  92 PASS   : testDate::initTestCase()
  93 PASS   : testDate::testValidity()
  94 FAIL!  : testDate::testMonth() Compared values are not the same
  95    Actual (date.month()): 4
  96    Expected (3): 3
  97     Loc: [tutorial1.cpp(25)]
  98 PASS   : testDate::cleanupTestCase()
  99 Totals: 3 passed, 1 failed, 0 skipped
 100 ********* Finished testing of testDate *********
 101 </code>
 102 
 103 ===Running selected tests===
 104 
 105 When the number of test functions increases, and some of the functions take a long time to run, it can be useful to only run a selected function. For example, if you only want to run the testMonth function, then you just specify that on the command line, as shown below:
 106 
 107 '''Example 5. QDate unit test output - selected function'''
 108 
 109 <syntaxhighlight lang="text">
 110 $ ./tutorial1 testValidity
 111 ********* Start testing of testDate *********
 112 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051003
 113 PASS   : testDate::initTestCase()
 114 PASS   : testDate::testValidity()
 115 PASS   : testDate::cleanupTestCase()
 116 Totals: 3 passed, 0 failed, 0 skipped
 117 ********* Finished testing of testDate *********        
 118 </code>
 119 
 120 Note that the initTestCase and cleanupTestCase routines are always run, so that any necessary setup and cleanup will still be done. 
 121 
 122 You can get a list of the available functions by passing the -functions option, as shown below: 
 123 
 124 '''Example 6. QDate unit test output - listing functions'''
 125 <syntaxhighlight lang="text">
 126 $ ./tutorial1 -functions
 127 testValidity()
 128 testMonth()
 129 </code>
 130 
 131 ===Verbose output options===
 132 
 133 You can get more verbose output by using the -v1, -v2 and -vs options. -v1 produces a message on entering each test function. I found this is useful when it looks like a test is hanging. This is shown below:
 134 
 135 '''Example 7. QDate unit test output - verbose output'''
 136 
 137 <syntaxhighlight lang="text">
 138 $ ./tutorial1 -v1
 139 ********* Start testing of testDate *********
 140 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051003
 141 INFO   : testDate::initTestCase() entering
 142 PASS   : testDate::initTestCase()
 143 INFO   : testDate::testValidity() entering
 144 PASS   : testDate::testValidity()
 145 INFO   : testDate::testMonth() entering
 146 PASS   : testDate::testMonth()
 147 INFO   : testDate::cleanupTestCase() entering
 148 PASS   : testDate::cleanupTestCase()
 149 Totals: 4 passed, 0 failed, 0 skipped
 150 ********* Finished testing of testDate *********
 151 </code>
 152 
 153 The -v2 option shows each QVERIFY, QCOMPARE and QTEST, as well as the message on entering each test function. I found this useful for verifying that a particular step is being run. This is shown below: 
 154 
 155 ''''Example 8. QDate unit test output - more verbose output'''
 156 
 157 <syntaxhighlight lang="text">
 158 $ ./tutorial1 -v2
 159 ********* Start testing of testDate *********
 160 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051003
 161 INFO   : testDate::initTestCase() entering
 162 PASS   : testDate::initTestCase()
 163 INFO   : testDate::testValidity() entering
 164 INFO   : testDate::testValidity() QVERIFY(date.isValid())
 165     Loc: [tutorial1.cpp(17)]
 166 PASS   : testDate::testValidity()
 167 INFO   : testDate::testMonth() entering
 168 INFO   : testDate::testMonth() COMPARE()
 169     Loc: [tutorial1.cpp(25)]
 170 INFO   : testDate::testMonth() COMPARE()
 171     Loc: [tutorial1.cpp(27)]
 172 PASS   : testDate::testMonth()
 173 INFO   : testDate::cleanupTestCase() entering
 174 PASS   : testDate::cleanupTestCase()
 175 Totals: 4 passed, 0 failed, 0 skipped
 176 ********* Finished testing of testDate *********        
 177 </code>
 178 
 179 The -vs option shows each signal that is emitted. In our example, there are no signals, so -vs has no effect. Getting a list of signals is useful for debugging failing tests, especially GUI tests which we will see in the third tutorial. 
 180 
 181 ===Output to a file===
 182 
 183 If you want to output the results of your testing to a file, you can use the -o filename, where you replace filename with the name of the file you want to save output to.
 184 
 185 ==Tutorial 2: Data driven testing of a date class==
 186 
 187 In the previous example, we looked at how we can test a date class. If we decided that we really needed to test a lot more dates, then we'd be cutting and pasting a lot of code. If we subsequently changed the name of a function, then it has to be changed in a lot of places. As an alternative to introducing these types of maintenance problems into our tests, QtTestLib offers support for data driven testing. 
 188 
 189 The easiest way to understand data driven testing is by an example, as shown below:
 190 
 191 '''Example 9. QDate test code, data driven version'''
 192 <syntaxhighlight lang="cpp-qt" line>
 193 #include <QtTest>
 194 #include <QtCore>
 195 
 196 
 197 class testDate: public QObject
 198 {
 199     Q_OBJECT
 200 private slots:
 201     void testValidity();
 202     void testMonth_data();
 203     void testMonth();
 204 };
 205 
 206 void testDate::testValidity()
 207 {
 208     // 12 March 1967
 209     QDate date( 1967, 3, 12 );
 210     QVERIFY( date.isValid() );
 211 }
 212 
 213 void testDate::testMonth_data()
 214 {
 215     QTest::addColumn<int>("year");  // the year we are testing
 216     QTest::addColumn<int>("month"); // the month we are testing
 217     QTest::addColumn<int>("day");   // the day we are testing
 218     QTest::addColumn<QString>("monthName");   // the name of the month
 219 
 220     QTest::newRow("1967/3/11") << 1967 << 3 << 11 << QString("March");
 221     QTest::newRow("1966/1/10") << 1966 << 1 << 10 << QString("January");
 222     QTest::newRow("1999/9/19") << 1999 << 9 << 19 << QString("September");
 223     // more rows of dates can go in here...
 224 }
 225 
 226 void testDate::testMonth()
 227 {
 228     QFETCH(int, year);
 229     QFETCH(int, month);
 230     QFETCH(int, day);
 231     QFETCH(QString, monthName);
 232 
 233     QDate date;
 234     date.setYMD( year, month, day);
 235     QCOMPARE( date.month(), month );
 236     QCOMPARE( QDate::longMonthName(date.month()), monthName );
 237 }
 238 
 239 
 240 QTEST_MAIN(testDate)
 241 #include "tutorial2.moc"
 242 </code>
 243 
 244 As you can see, we've introduced a new method - testMonth_data, and moved the specific test date out of testMonth. We've had to add some more code (which will be explained soon), but the result is a separation of the data we are testing, and the code we are using to test it. 
 245 The names of the functions are important - you must use the _data suffix for the data setup routine, and the first part of the data setup routine must match the name of the driver routine. 
 246 
 247 It is useful to visualise the data as being a table, where the columns are the various data values required for a single run through the driver, and the rows are different runs. In our example, there are four columns (three integers, one for each part of the date; and one  QString ), added in lines 19 through 22. The addColumn template obviously requires the type of variable to be added, and also requires a variable name argument. We then add as many rows as required using the newRow function, as shown in lines 23 through 26. The string argument to newRow is a label, which is handy for determining what is going on with failing tests, but doesn't have any effect on the test itself. 
 248 
 249 To use the data, we simply use QFETCH to obtain the appropriate data from each row. The arguments to QFETCH are the type of the variable to fetch, and the name of the column (which is also the local name of the variable it gets fetched into). You can then use this data in a QCOMPARE or QVERIFY check. The code is run for each row, which you can see below:
 250 
 251 '''Example 10. Results of data driven testing, showing QFETCH'''
 252 
 253 <syntaxhighlight lang="text">
 254 $ ./tutorial2 -v2
 255 ********* Start testing of testDate *********
 256 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051020
 257 INFO   : testDate::initTestCase() entering
 258 PASS   : testDate::initTestCase()
 259 INFO   : testDate::testValidity() entering
 260 INFO   : testDate::testValidity() QVERIFY(date.isValid())
 261    Loc: [tutorial2.cpp(19)]
 262 PASS   : testDate::testValidity()
 263 INFO   : testDate::testMonth() entering
 264 INFO   : testDate::testMonth(1967/3/11) COMPARE()
 265    Loc: [tutorial2.cpp(44)]
 266 INFO   : testDate::testMonth(1967/3/11) COMPARE()
 267    Loc: [tutorial2.cpp(45)]
 268 INFO   : testDate::testMonth(1966/1/10) COMPARE()
 269    Loc: [tutorial2.cpp(44)]
 270 INFO   : testDate::testMonth(1966/1/10) COMPARE()
 271    Loc: [tutorial2.cpp(45)]
 272 INFO   : testDate::testMonth(1999/9/19) COMPARE()
 273    Loc: [tutorial2.cpp(44)]
 274 INFO   : testDate::testMonth(1999/9/19) COMPARE()
 275    Loc: [tutorial2.cpp(45)]
 276 PASS   : testDate::testMonth()
 277 INFO   : testDate::cleanupTestCase() entering
 278 PASS   : testDate::cleanupTestCase()
 279 Totals: 4 passed, 0 failed, 0 skipped
 280 ********* Finished testing of testDate *********
 281 </code>
 282 
 283 === The QTEST macro ===
 284 
 285 As an alternative to using QFETCH and QCOMPARE, you may be able to use the QTEST macro instead. QTEST takes two arguments, and if one is a string, it looks up that string as an argument in the current row. You can see how this can be used below, which is equivalent to the testMonth() code in the previous example. 
 286 
 287 '''Example 11. QDate test code, data driven version using QTEST'''
 288 
 289 <syntaxhighlight lang="cpp-qt" line>
 290 void testDate::testMonth()
 291 {
 292     QFETCH(int, year);
 293     QFETCH(int, month);
 294     QFETCH(int, day);
 295 
 296     QDate date;
 297     date.setYMD( year, month, day);
 298     QCOMPARE( date.month(), month );
 299     QTEST( QDate::longMonthName(date.month()), "monthName" );
 300 }      
 301 </code>
 302 
 303 In the example above, note that monthName is enclosed in quotes, and we no longer have a QFETCH call for monthName. 
 304 
 305 The other QCOMPARE could also have been converted to use QTEST, however this would be less efficient, because we already needed to use QFETCH to get month for the setYMD in the line above.
 306 
 307 ===Running selected tests with selected data===
 308 
 309 In the previous tutorial, we saw how to run a specific test by specifying the name of the test as a command line argument. In data driven testing, you can select which data you want the test run with, by adding a colon and the label for the data row. For example, if we just want to run the testMonth test for the first row, we would use <pre>./tutorial2 -v2 testMonth:1967/3/11</pre>. The result of this is shown below.
 310 
 311 '''Example 12. QDate unit test output - selected function and data'''
 312 <syntaxhighlight lang="text">
 313 $ ./tutorial2 -v2 testMonth:1967/3/11
 314 ********* Start testing of testDate *********
 315 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051020
 316 INFO   : testDate::initTestCase() entering
 317 PASS   : testDate::initTestCase()
 318 INFO   : testDate::testMonth() entering
 319 INFO   : testDate::testMonth(1967/3/11) COMPARE()
 320    Loc: [tutorial2.cpp(44)]
 321 INFO   : testDate::testMonth(1967/3/11) COMPARE()
 322    Loc: [tutorial2.cpp(45)]
 323 PASS   : testDate::testMonth()
 324 INFO   : testDate::cleanupTestCase() entering
 325 PASS   : testDate::cleanupTestCase()
 326 Totals: 3 passed, 0 failed, 0 skipped
 327 ********* Finished testing of testDate *********
 328 </code>
 329 
 330 ==Tutorial 3: Testing Graphical User Interfaces==
 331 
 332 In the previous two tutorials, we've tested a date management class. This is an pretty typical use of unit testing. However Qt and KDE applications will make use graphical classes that take user input (typically from a keyboard and mouse). QtTestLib offers support for testing these classes, which we'll see in this tutorial. 
 333 
 334 Again, we'll use an existing class as our test environment, and again it will be date related - the standard Qt {{qt|QDateEdit}} class. For those not familiar with this class, it is a simple date entry widget (although with some powerful back end capabilities). A picture of the widget is shown below. 
 335 
 336 [[Image:Qdateedit_dlg.png|thumb|'''Figure 1. QDateEdit widget screenshot''']]
 337 
 338 The way QtTestLib provides GUI testing is by injecting {{qt|QInputEvent}} events. To the application, these input events appear the same as normal key press/release and mouse clicks/drags. However the mouse and keyboard are unaffected, so that you can continue to use the machine normally while tests are being run. 
 339 
 340 An example of how you can use the GUI functionality of QtTestLib is shown below.
 341 
 342 '''Example 13. QDateEdit test code'''
 343 
 344 <syntaxhighlight lang="cpp-qt" line>
 345 #include <QtTest>
 346 #include <QtCore>
 347 #include <QtGui>
 348 Q_DECLARE_METATYPE(QDate)
 349 
 350 class testDateEdit: public QObject
 351 {
 352     Q_OBJECT
 353 private slots:
 354     void testChanges();
 355     void testValidator_data();
 356     void testValidator();
 357 };
 358 
 359 void testDateEdit::testChanges()
 360 {
 361     // 11 March 1967
 362     QDate date( 1967, 3, 11 );
 363     QDateEdit dateEdit( date );
 364 
 365     // up-arrow should increase day by one
 366     QTest::keyClick( &dateEdit, Qt::Key_Up );
 367     QCOMPARE( dateEdit.date(), date.addDays(1) );
 368 
 369     // we click twice on the "reduce" arrow at the bottom RH corner
 370     // first we need the widget size to know where to click
 371     QSize editWidgetSize = dateEdit.size();
 372     QPoint clickPoint(editWidgetSize.rwidth()-2, editWidgetSize.rheight()-2);
 373     // issue two clicks
 374     QTest::mouseClick( &dateEdit, Qt::LeftButton, 0, clickPoint);
 375     QTest::mouseClick( &dateEdit, Qt::LeftButton, 0, clickPoint);
 376     // and we should have decreased day by two (one less than original)
 377     QCOMPARE( dateEdit.date(), date.addDays(-1) );
 378 
 379     QTest::keyClicks( &dateEdit, "25122005" );
 380     QCOMPARE( dateEdit.date(), QDate( 2005, 12, 25 ) );
 381 
 382     QTest::keyClick( &dateEdit, Qt::Key_Tab, Qt::ShiftModifier );
 383     QTest::keyClicks( &dateEdit, "08" );
 384     QCOMPARE( dateEdit.date(), QDate( 2005, 8, 25 ) );
 385 }
 386 
 387 void testDateEdit::testValidator_data()
 388 {
 389     qRegisterMetaType<QDate>("QDate");
 390 
 391     QTest::addColumn<QDate>( "initialDate" );
 392     QTest::addColumn<QString>( "keyclicks" );
 393     QTest::addColumn<QDate>( "finalDate" );
 394 
 395     QTest::newRow( "1968/4/12" ) << QDate( 1967, 3, 11 )
 396                                  << QString( "12041968" )
 397                                  << QDate( 1968, 4, 12 );
 398 
 399     QTest::newRow( "1967/3/14" ) << QDate( 1967, 3, 11 )
 400                                  << QString( "140abcdef[" )
 401                                  << QDate( 1967, 3, 14 );
 402     // more rows can go in here
 403 }
 404 
 405 void testDateEdit::testValidator()
 406 {
 407     QFETCH( QDate, initialDate );
 408     QFETCH( QString, keyclicks );
 409     QFETCH( QDate, finalDate );
 410 
 411     QDateEdit dateEdit( initialDate );
 412     // this next line is just to start editing
 413     QTest::keyClick( &dateEdit, Qt::Key_Enter );
 414     QTest::keyClicks( &dateEdit, keyclicks );
 415     QCOMPARE( dateEdit.date(), finalDate );
 416 }
 417 
 418 QTEST_MAIN(testDateEdit)
 419 #include "tutorial3.moc"
 420 </code>
 421 
 422 Much of this code is common with previous examples, so I'll focus on the new elements and the more important changes as we work through the code line-by-line. 
 423 
 424 Lines 1 to 3 import the various Qt declarations, as before. 
 425 
 426 Line 4 is a macro that is required for the data-driven part of this test, which I'll come to soon. 
 427 
 428 Lines 5 to 12 declare the test class - while the names have changed, it is pretty similar to the previous example. Note the testValidator and testValidator_data functions - we will be using data driven testing again in this example. 
 429 
 430 Our first real test starts in line 13. Line 16 creates a QDate, and line 17 uses that date as the initial value for a QDateEdit widget. 
 431 
 432 Lines 19 and 20 show how we can test what happens when we press the up-arrow key. The QTest::keyClick function takes a pointer to a widget, and a symbolic key name (a char or a Qt::Key). At line 20, we check that the effect of that event was to increment the date by a day. The QTest:keyClick function also takes an optional keyboard modifier (such as Qt::ShiftModifier for the shift key) and an optional delay value (in milliseconds). As an alternative to using QTest::keyClick, you can use QTest::keyPress and QTest::keyRelease to construct more complex keyboard sequences. 
 433 
 434 Lines 23 to 29 show a similar test to the previous one, but in this case we are simulating a mouse click. We need to click in the lower right hand part of the widget (to hit the decrement arrow - see Figure 1), and that requires knowing how large the widget is. So lines 23 and 24 calculate the correct point based off the size of the widget. Line 26 (and the identical line 27) simulates clicking with the left-hand mouse button at the calculated point. The arguments to Qt::mouseClick are:
 435 
 436 *a pointer to the widget that the click event should be sent to.
 437 *the mouse button that is being clicked.
 438 *an optional keyboard modifier (such a Qt::ShiftModifier), or 0 for no modifiers.
 439 *an optional click point - this defaults to the middle of the widget if not specified.
 440 *an optional mouse delay.
 441 
 442 In addition to QTest::mouseClick, there is also QTest::mousePress, QTest::mouseRelease, QTest::mouseDClick (providing double-click) and QTest::mouseMove. The first three are used in the same way as QTest::mouseClick. The last takes a point to move the mouse to. You can use these functions in combination to simulate dragging with the mouse. 
 443 
 444 Lines 30 and 31 show another approach to keyboard entry, using the QTest::keyClicks. Where QTest::keyClick sends a single key press, QTest::keyClicks takes a QString (or something equivalent, in line 30 a character array) that represents a sequence of key clicks to send. The other arguments are the same. 
 445 
 446 Lines 32 to 34 show how you may need to use a combination of functions. After we've entered a new date in line 30, the cursor is at the end of the widget. At line 32, we use a Shift-Tab combination to move the cursor back to the month value. Then at line 33 we enter a new month value. Of course we could have used individual calls to QTest::keyClick, however that wouldn't have been as clear, and would also have required more code.
 447 
 448 === Data-driven GUI testing ===
 449 
 450 Lines 50 to 60 show a data-driven test - in this case we are checking that the validator on QDateEdit is performing as expected. This is a case where data-driven testing can really help to ensure that things are working the way they should. 
 451 
 452 At lines 52 to 54, we fetch in an initial value, a series of key-clicks, and an expected result. These are the columns that are set up in lines 39 to 41. However note that we are now pulling in a QDate, where in previous examples we used three integers and then build the QDate from those. However QDate isn't a registered type for {{qt|QMetaType}}, and so we need to register it before we can use it in our data-driven testing. This is done using the Q_DECLARE_METATYPE macro in line 4 and the qRegisterMetaType function in line 38. 
 453 
 454 Lines 42 to 47 add in a couple of sample rows. Lines 42 to 44 represent a case where the input is valid, and lines 45 to 47 are a case where the input is only partly valid (the day part). A real test will obviously contain far more combinations than this. 
 455 
 456 Those test rows are actually tested in lines 55 to 59. We construct the QDateEdit widget in line 55, using the initial value. We then send an Enter key click in line 57, which is required to get the widget into edit mode. At line 58 we simulate the data entry, and at line 59 we check whether the results are what was expected. 
 457 
 458 Lines 61 and 62 are the same as we've seen in previous examples.
 459 
 460 === Re-using test elements ===
 461 
 462 If you are re-using a set of events a number of times, then it may be an advantage to build a list of events, and then just replay them. This can improve maintainability and clarity of a set of tests, especially for mouse movements. 
 463 
 464 The key class for building a list of test events is imaginatively known as QTestEventList. It is a QList of QTestEvents. The normal approach is to create the list, and then use various member functions to add key and mouse events. The normal functions that you'll need are addKeyClick and addMouseClick, which are very similar to the QTest::keyClick and QTest::mouseClick functions we used earlier in this tutorial. For finer grained operations, you can also use addKeyPress, addKeyRelease, addKeyEvent, addMousePress, addMouseRelease, addMouseDClick and addMouseMove to build up more complex event lists. You can also use addDelay to add a specified delay between events. When the list has been built up, you just call simulate on each widget. 
 465 
 466 You can see how this works in the example below, which is the QDateEdit example (from above) converted to use QTestEventList.
 467 
 468 '''Example 14. QDateEdit test code, using QTestEventList'''
 469 <syntaxhighlight lang="cpp-qt" line>       
 470 #include <QtTest>
 471 #include <QtCore>
 472 #include <QtGui>
 473 Q_DECLARE_METATYPE(QDate)
 474 
 475 class testDateEdit: public QObject
 476 {
 477     Q_OBJECT
 478 private slots:
 479     void testChanges();
 480     void testValidator_data();
 481     void testValidator();
 482 };
 483 
 484 void testDateEdit::testChanges()
 485 {
 486     // 11 March 1967
 487     QDate date( 1967, 3, 11 );
 488     QDateEdit dateEdit( date );
 489 
 490     // up-arrow should increase day by one
 491     QTest::keyClick( &dateEdit, Qt::Key_Up );
 492     QCOMPARE( dateEdit.date(), date.addDays(1) );
 493 
 494     // we click twice on the "reduce" arrow at the bottom RH corner
 495     // first we need the widget size to know where to click
 496     QSize editWidgetSize = dateEdit.size();
 497     QPoint clickPoint(editWidgetSize.rwidth()-2, editWidgetSize.rheight()-2);
 498     // build a list that contains two clicks
 499     QTestEventList list1;
 500     list1.addMouseClick( Qt::LeftButton, 0, clickPoint);
 501     list1.addMouseClick( Qt::LeftButton, 0, clickPoint);
 502     // call that list on the widget
 503     list1.simulate( &dateEdit );
 504     // and we should have decreased day by two (one less than original)
 505     QCOMPARE( dateEdit.date(), date.addDays(-1) );
 506 
 507     QTest::keyClicks( &dateEdit, "25122005" );
 508     QCOMPARE( dateEdit.date(), QDate( 2005, 12, 25 ) );
 509 
 510     QTestEventList list2;
 511     list2.addKeyClick( Qt::Key_Tab, Qt::ShiftModifier );
 512     list2.addKeyClicks( "08" );
 513     list2.simulate( &dateEdit );
 514     QCOMPARE( dateEdit.date(), QDate( 2005, 8, 25 ) );
 515 }
 516 
 517 void testDateEdit::testValidator_data()
 518 {
 519     qRegisterMetaType<QDate>("QDate");
 520 
 521     QTest::addColumn<QDate>( "initialDate" );
 522     QTest::addColumn<QTestEventList>( "events" );
 523     QTest::addColumn<QDate>( "finalDate" );
 524 
 525     QTestEventList eventsList1;
 526     // this next line is just to start editing
 527     eventsList1.addKeyClick( Qt::Key_Enter );
 528     eventsList1.addKeyClicks( "12041968" );
 529 
 530     QTest::newRow( "1968/4/12" ) << QDate( 1967, 3, 11 )
 531                                  << eventsList1
 532                                  << QDate( 1968, 4, 12 );
 533 
 534     QTestEventList eventsList2;
 535     eventsList2.addKeyClick( Qt::Key_Enter );
 536     eventsList2.addKeyClicks( "140abcdef[" );
 537 
 538     QTest::newRow( "1967/3/14" ) << QDate( 1967, 3, 11 )
 539                                  << eventsList2
 540                                  << QDate( 1967, 3, 14 );
 541     // more rows can go in here
 542 }
 543 
 544 void testDateEdit::testValidator()
 545 {
 546     QFETCH( QDate, initialDate );
 547     QFETCH( QTestEventList, events );
 548     QFETCH( QDate, finalDate );
 549 
 550     QDateEdit dateEdit( initialDate );
 551 
 552     events.simulate( &dateEdit);
 553 
 554     QCOMPARE( dateEdit.date(), finalDate );
 555 }
 556 
 557 QTEST_MAIN(testDateEdit)
 558 #include "tutorial3a.moc"
 559 </code>
 560 
 561 This example is pretty much the same as the previous version, up to line 25. In line 26, we create a QTestEventList. We add events to the list in lines 27 and 28 - note that we don't specify the widget we are calling them on at this stage. In line 30, we simulate each event on the widget. If we had multiple widgets, we could call simulate using the same set of events. 
 562 
 563 Lines 31 to 34 are as per the previous example. 
 564 
 565 We create another list in lines 35 to 37, although this time we are using addKeyClick and addKeyClicks instead of adding mouse events. Note that an event list can contain combinations of mouse and keyboard events - it just didn't make sense in this test to have such a combination. We use the second list at line 38, and check the results in line 39. 
 566 
 567 You can also build lists of events in data driven testing as well, as shown in lines 41 to 70. The key difference is that instead of fetching a QString in each row, we are fetching a QTestEventList. This requires that we add a column of QTestEventList, rather than QString (see line 45). At lines 47 to 50, we create a list of events. At line 52 we add those events to the applicable row. We create a second list at lines 54 to 56, and add that second list to the applicable row in line 58. 
 568 
 569 We fetch the events in line 65, and use them in line 68. If we had multiple widgets, then we could use the same event list several times.
 570 
 571 ==Tutorial 4 - Testing for failure and avoiding tests==
 572 
 573 Under some conditions, it is impossible to avoid tests failing. In this section, we'll see how to deal with these cases. 
 574 
 575 ===Skipping tests===
 576 
 577 Where a test doesn't make sense to run (for example, if the required test files aren't available, or the feature is architecture or operating system dependent), the cleanest solution is to skip the test. 
 578 
 579 Tests are skipped using the QSKIP macro. QSKIP takes two arguments - a label string that should be used to describe why the test is being skipped, and a enumerated constant that controls how much of the test is skipped. If you pass SkipSingle, and the test is data driven, then only the current row is skipped. If you pass SkipAll and the test is data driven, then all following rows are skipped. If the test is not data driven, then it doesn't matter which one is used. 
 580 
 581 You can see how QSKIP works in the example below:
 582 
 583 '''Example 15. Unit test showing skipped tests'''
 584 <syntaxhighlight lang="text">
 585 void testDate::testSkip_data()
 586 {
 587     QTest::addColumn<int>("val1");
 588     QTest::addColumn<int>("val2");
 589 
 590     QTest::newRow("1") << 1 << 1;
 591     QTest::newRow("2") << 1 << 2;
 592     QTest::newRow("3") << 3 << 3;
 593     QTest::newRow("5") << 5 << 5;
 594     QTest::newRow("4") << 4 << 5;
 595 }
 596 
 597 void testDate::testSkip()
 598 {
 599     QFETCH(int, val1);
 600     QFETCH(int, val2);
 601 
 602     if ( val2 == 2 ) 
 603         QSKIP("Two isn't good, not doing it", SkipSingle);
 604     if ( val1 == 5 )
 605         QSKIP("Five! I've had enough, bailing here", SkipAll);
 606     QCOMPARE( val1, val2 );
 607 }
 608 </code>
 609 
 610 '''Example 16. Output of unit test showing skipped tests'''
 611 <syntaxhighlight lang="text">
 612 $ ./tutorial4 testSkip -v2
 613 ********* Start testing of testDate *********
 614 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051107
 615 INFO   : testDate::initTestCase() entering
 616 PASS   : testDate::initTestCase()
 617 INFO   : testDate::testSkip() entering
 618 INFO   : testDate::testSkip(1) COMPARE()
 619    Loc: [tutorial4.cpp(82)]
 620 SKIP   : testDate::testSkip(2) Two isn't good, not doing it
 621    Loc: [tutorial4.cpp(79)]
 622 INFO   : testDate::testSkip(3) COMPARE()
 623    Loc: [tutorial4.cpp(82)]
 624 SKIP   : testDate::testSkip(5) Five! I've had enough, bailing here
 625    Loc: [tutorial4.cpp(81)]
 626 PASS   : testDate::testSkip()
 627 INFO   : testDate::cleanupTestCase() entering
 628 PASS   : testDate::cleanupTestCase()
 629 Totals: 3 passed, 0 failed, 2 skipped
 630 ********* Finished testing of testDate *********
 631 </code>
 632 
 633 from the verbose output, you can see that the test was run on the first and third rows. The second row wasn't run because of the QSKIP call with a SkipSingle argument. Similarly, the fourth and fifth rows weren't run because the fourth row triggered a QSKIP call with a SkipAll argument. 
 634 
 635 Also note that the test didn't fail, even though there were two calls to QSKIP. Conceptually, a skipped test is a test that didn't make sense to run for test validity reasons, rather than a test that is valid but will fail because of bugs or lack of features in the code being tested. 
 636 
 637 ===Handling expected failures===
 638 
 639 If you have valid tests, but the code that you are testing doesn't pass them, then ideally you fix the code you are testing. However sometimes that isn't possible in the time that you have available, or because of a need to avoid binary incompatible changes. In this case, it is undesirable to delete or modify the unit tests - it is better to flag the test as "expected to fail", using the QEXPECT_FAIL macro. An example of this is shown below: 
 640 
 641 '''Example 17. Unit test showing expected failures'''
 642 
 643 <syntaxhighlight lang="cpp-qt" line>
 644 void testDate::testExpectedFail()
 645 {
 646     QEXPECT_FAIL("", "1 is not 2, even for very large 1", Continue);
 647     QCOMPARE( 1, 2 );
 648     QCOMPARE( 2, 2 );
 649 
 650     QEXPECT_FAIL("", "1 is not 2, even for very small 2", Abort);
 651     QCOMPARE( 1, 2 );
 652     // The next line will not be run, because we Abort on previous failure
 653     QCOMPARE( 3, 3 );
 654 }
 655 </code>
 656 
 657 '''Example 18. Output of unit test showing expected failures'''
 658 
 659 <syntaxhighlight lang="text">
 660 $ ./tutorial4/tutorial4 testExpectedFail -v2
 661 ********* Start testing of testDate *********
 662 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051107
 663 INFO   : testDate::initTestCase() entering
 664 PASS   : testDate::initTestCase()
 665 INFO   : testDate::testExpectedFail() entering
 666 INFO   : testDate::testExpectedFail() Compared values are not the same
 667    Actual (1): 1
 668    Expected (2): 2
 669    Loc: [tutorial4.cpp(41)]
 670 XFAIL  : testDate::testExpectedFail() 1 is not 2, even for very large 1
 671    Loc: [tutorial4.cpp(41)]
 672 INFO   : testDate::testExpectedFail() COMPARE()
 673    Loc: [tutorial4.cpp(42)]
 674 INFO   : testDate::testExpectedFail() Compared values are not the same
 675    Actual (1): 1
 676    Expected (2): 2
 677    Loc: [tutorial4.cpp(45)]
 678 XFAIL  : testDate::testExpectedFail() 1 is not 2, even for very small 2
 679    Loc: [tutorial4.cpp(45)]
 680 PASS   : testDate::testExpectedFail()
 681 INFO   : testDate::cleanupTestCase() entering
 682 PASS   : testDate::cleanupTestCase()
 683 Totals: 3 passed, 0 failed, 0 skipped
 684 ********* Finished testing of testDate *********
 685 </code>
 686 
 687 As you can see from the verbose output, we expect a failure from each time we do a QCOMPARE( 1, 2);. In the first call to QEXPECT_FAIL, we use the Continue argument, so the rest of the tests will still be run. However in the second call to QEXPECT_FAILwe use the Abort and the test bails at this point. Generally it is better to use Continue unless you have a lot of closely related tests that would each need a QEXPECT_FAIL entry. 
 688 
 689 Also note that tests that are marked as expected failures are not considered to be failures, so the test function above is considered to be a pass. 
 690 
 691 If a test that is marked to be an expected failure, and it unexpectedly passes, then that is flagged as an error, as shown below: 
 692 
 693 '''Example 19. Unit test showing unexpected pass'''
 694 <syntaxhighlight lang="cpp-qt" line>
 695 void testDate::testUnexpectedPass()
 696 {
 697     QEXPECT_FAIL("", "1 is not 2, even for very large 1", Continue);
 698     QCOMPARE( 1, 1 );
 699     QCOMPARE( 2, 2 );
 700 
 701     QEXPECT_FAIL("", "1 is not 2, even for very small 2", Abort);
 702     QCOMPARE( 1, 1 );
 703     QCOMPARE( 3, 3 );
 704 }
 705 </code>
 706 
 707 '''Example 20. Output of unit test showing unexpected pass'''
 708 
 709 <syntaxhighlight lang="text">
 710 $ ./tutorial4/tutorial4 testUnexpectedPass -v2
 711 ********* Start testing of testDate *********
 712 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051107
 713 INFO   : testDate::initTestCase() entering
 714 PASS   : testDate::initTestCase()
 715 INFO   : testDate::testUnexpectedPass() entering
 716 INFO   : testDate::testUnexpectedPass() COMPARE()
 717    Loc: [tutorial4.cpp(53)]
 718 XPASS  : testDate::testUnexpectedPass() COMPARE()
 719    Loc: [tutorial4.cpp(53)]
 720 INFO   : testDate::testUnexpectedPass() COMPARE()
 721    Loc: [tutorial4.cpp(54)]
 722 INFO   : testDate::testUnexpectedPass() COMPARE()
 723    Loc: [tutorial4.cpp(57)]
 724 XPASS  : testDate::testUnexpectedPass() COMPARE()
 725    Loc: [tutorial4.cpp(57)]
 726 INFO   : testDate::cleanupTestCase() entering
 727 PASS   : testDate::cleanupTestCase()
 728 Totals: 2 passed, 2 failed, 0 skipped
 729 ********* Finished testing of testDate *********
 730 </code>
 731 
 732 The effect of unexpected passes on the running of the test is controlled by the second argument to QEXPECT_FAIL. If the argument is Continue and the test unexpectedly passes, then the rest of the test function will be run. If the argument is Abort, then the test will stop.
 733 
 734 ===Checking debug messages and warnings===
 735 
 736 If you are testing border cases, you will likely run across the case where some kind of message will be produced using the qDebug or qWarning functions. Where a test produces a debug or warning message, that message will be logged in the test output (although it will still be considered a pass unless some other check fails), as shown in the example below: 
 737 
 738 Example 21. Unit test producing warning and debug messages
 739 <syntaxhighlight lang="cpp-qt" line>
 740 void testDate::testQdebug()
 741 {
 742     qWarning("warning");
 743     qDebug("debug");
 744     qCritical("critical");
 745 }
 746 </code>   
 747 
 748 '''Example 22. Output of unit test producing warning and debug messages'''
 749 <syntaxhighlight lang="text">
 750 $ ./tutorial4 testQdebug
 751 ********* Start testing of testDate *********
 752 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051107
 753 PASS   : testDate::initTestCase()
 754 QWARN  : testDate::testQdebug() warning
 755 QDEBUG : testDate::testQdebug() debug
 756 QSYSTEM: testDate::testQdebug() critical
 757 PASS   : testDate::testQdebug()
 758 PASS   : testDate::cleanupTestCase()
 759 Totals: 3 passed, 0 failed, 0 skipped
 760 ********* Finished testing of testDate *********
 761 </code>        
 762 
 763 Note that while this example produces the debug and warning messages within the test function (testQdebug), those messages would normally be propagated up from the code being tested. However the source of the messages does not make any difference to how they are handled. 
 764 
 765 If your test needs include either a clean output, or verification that appropriate messages are generated, then you will probably need the QtTest::ignoreMessage function. 
 766 
 767 {{Tip|'''Note:''' The ignoreMessage function can be used to ignore a message, however it might be clearer to think of this function as checking for the presence of an expected message. In particular, it is a test failure if you call ignoreMessage and the message is not generated.}}
 768 
 769 An example of how ignoreMessage works is shown below. 
 770 
 771 '''Example 23. Example of using ignoreMessage'''
 772 <syntaxhighlight lang="cpp-qt" line>
 773 void testDate::testValidity()
 774 {
 775     QTest::ignoreMessage(QtWarningMsg, "validity warning");
 776     qWarning("validity warning");
 777 }
 778 
 779 void testDate::testValiditi()
 780 {
 781     QTest::ignoreMessage(QtWarningMsg, "validity warning");
 782     qWarning("validiti warning");
 783 }
 784 </code>
 785 
 786         
 787 '''Example 24. Output of ignoreMessage example'''
 788 <syntaxhighlight lang="text">
 789 $ ./tutorial4 testValidity testValiditi
 790 ********* Start testing of testDate *********
 791 Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051107
 792 PASS   : testDate::initTestCase()
 793 PASS   : testDate::testValidity()
 794 QWARN  : testDate::testValiditi() validiti warning
 795 INFO   : testDate::testValiditi() Did not receive message: "validity warning"
 796 FAIL!  : testDate::testValiditi() Not all expected messages were received
 797 PASS   : testDate::cleanupTestCase()
 798 Totals: 3 passed, 1 failed, 0 skipped
 799 ********* Finished testing of testDate *********
 800 </code>   
 801 
 802 Note that the warning message in testDate::testValidity has been "swallowed" by the the call to ignoreMessage. 
 803 
 804 By contrast, the warning message in testDate::testValiditi still causes a warning to be logged, because the ignoreMessage call does not match the text in the warning message. In addition, because a we expected a particular warning message and it wasn't received, the testDate::testValiditi test function fails.
 805 
 806 ==Tutorial 5: Testing Qt slots and signals==
 807 
 808 An important part of Qt programming is the use of signals and slots. This section covers the support for testing of these features. 
 809 
 810 {{Tip|'''Note:''' If you are not familiar with Qt signals and slots, you probably should review the introduction to this feature provided in your Qt documentation. It is also available at http://doc.trolltech.com/latest/signalsandslots.html. You also might like to review Tutorial 5 in your Qt documentation. This tutorial is also available at http://doc.trolltech.com/latest/tutorial-t5.html.}}
 811 
 812 ===Testing slots===
 813 
 814 Testing slots is very easy, because a slot is just a specially annotated method. You can call slots just like any other method you'd like to test, as shown below: 
 815 
 816 '''Example 25. QLabel test code, showing testing of a couple of slots'''
 817 <syntaxhighlight lang="cpp-qt" line>
 818 #include <QtTest>
 819 #include <QtCore>
 820 #include <QtGui>
 821 
 822 class testLabel: public QObject
 823 {
 824     Q_OBJECT
 825 private slots:
 826     void testChanges();
 827 };
 828 
 829 void testLabel::testChanges()
 830 {
 831     QLabel label;
 832 
 833     // setNum() is a QLabel slot, but we can just call it like any
 834     // other method.
 835     label.setNum( 3 );
 836     QCOMPARE( label.text(), QString("3") );
 837 
 838     // clear() is also a slot.
 839     label.clear();
 840     QVERIFY( label.text().isEmpty() );
 841 }
 842 
 843 QTEST_MAIN(testLabel)
 844 #include "tutorial5.moc"
 845 </code>
 846 
 847 ===Testing signals===
 848 Testing of signals is a little more difficult than testing of slots, however Qt offers a very useful class called QSignalSpy that helps a lot. 
 849 
 850 {{qt|QSignalSpy}} is a class provided with Qt that allows you to record the signals that have been emitted from a particular QObject subclass object. You can then check that the right number of signals have been emitted, and that the right kind of signals were emitted. You can find more information on the QSignalSpy class in your Qt documentation.
 851 
 852 An example of how you can use QSignalSpy to test a class that has signals is shown below. 
 853 
 854 '''Example 26. QCheckBox test code, showing testing of signals'''
 855 
 856 <syntaxhighlight lang="cpp-qt" line>
 857 #include <QtTest>
 858 #include <QtCore>
 859 #include <QtGui>
 860 
 861 class testCheckBox: public QObject
 862 {
 863     Q_OBJECT
 864 private slots:
 865     void testSignals();
 866 };
 867 
 868 void testCheckBox::testSignals()
 869 {
 870     // You don't need to use an object created with "new" for
 871     // QSignalSpy, I just needed it in this case to test the emission
 872     // of a destroyed() signal.
 873     QCheckBox *xbox = new QCheckBox;
 874 
 875     // We are going to have two signal monitoring classes in use for
 876     // this test.
 877     // The first monitors the stateChanged() signal. 
 878     // Also note that QSignalSpy takes a pointer to the object.
 879     QSignalSpy stateSpy( xbox, SIGNAL( stateChanged(int) ) );
 880     
 881     // Not strictly necessary, but I like to check that I have set up
 882     // my QSignalSpy correctly.
 883     QVERIFY( stateSpy.isValid() );
 884 
 885     // Now we check to make sure we don't have any signals already
 886     QCOMPARE( stateSpy.count(), 0 );
 887 
 888     // Here is a second monitoring class - this one for the
 889     // destroyed() signal.
 890     QSignalSpy destroyedSpy( xbox, SIGNAL( destroyed() ) );
 891     QVERIFY( destroyedSpy.isValid() );
 892 
 893     // A sanity check to verify the initial state
 894     // This also shows that you can mix normal method checks with
 895     // signal checks.
 896     QCOMPARE( xbox->checkState(), Qt::Unchecked );
 897 
 898     // Shouldn't already have any signals
 899     QCOMPARE( destroyedSpy.count(), 0 );
 900 
 901     // If we change the state, we should get a signal.
 902     xbox->setCheckState( Qt::Checked );
 903     QCOMPARE( stateSpy.count(), 1 );
 904 
 905     xbox->setCheckState( Qt::Unchecked );
 906     QCOMPARE( stateSpy.count(), 2 );
 907 
 908     xbox->setCheckState( Qt::PartiallyChecked );
 909     QCOMPARE( stateSpy.count(), 3 );
 910 
 911     // If we destroy the object, the signal should be emitted.
 912     delete xbox;
 913 
 914     // So the count of objects should increase.
 915     QCOMPARE( destroyedSpy.count(), 1 );
 916 
 917     // We can also review the signals that we collected
 918     // QSignalSpy is really a QList of QLists, so we take the first
 919     // list, which corresponds to the arguments for the first signal
 920     // we caught.
 921     QList<QVariant> firstSignalArgs = stateSpy.takeFirst();
 922     // stateChanged() only has one argument - an enumerated type (int)
 923     // So we take that argument from the list, and turn it into an integer.
 924     int firstSignalState = firstSignalArgs.at(0).toInt();
 925     // We can then check we got the right kind of signal.
 926     QCOMPARE( firstSignalState, static_cast<int>(Qt::Checked) );
 927 
 928     // check the next signal - note that takeFirst() removes from the list
 929     QList<QVariant> nextSignalArgs = stateSpy.takeFirst();
 930     // this shows another way of fudging the argument types
 931     Qt::CheckState nextSignalState = (Qt::CheckState)nextSignalArgs.at(0).toInt();
 932     QCOMPARE( nextSignalState, Qt::Unchecked );
 933 
 934     // and again for the third signal
 935     nextSignalArgs = stateSpy.takeFirst();
 936     nextSignalState = (Qt::CheckState)nextSignalArgs.at(0).toInt();
 937     QCOMPARE( nextSignalState, Qt::PartiallyChecked );
 938 }
 939 
 940 QTEST_MAIN(testCheckBox)
 941 #include "tutorial5a.moc"
 942 </code>
 943 
 944 The first 11 lines are essentially unchanged from previous examples that we've seen. Line 15 creates the object that will be tested - as noted in the comments in lines 12-14, the only reason that I'm creating it with new is because I need to delete it in line 45 to cause the destroyed() signal to be emitted. 
 945 
 946 Line 20 sets up the first of our two QSignalSpy instances. The one in line 20 monitors the stateChanged(int) signal, and the one in line 29 monitors the destroyed() signal. If you get the name or signature of the signal wrong (for example, if you use stateChanged() instead of stateChanged(int)), then this will not be caught at compile time, but will result in a runtime failure. You can test if things were set up correctly using the isValid(), as shown in lines 24 and 30. 
 947 
 948 As shown in line 34, there is no reason why you cannot test normal methods, signals and slots in the same test. 
 949 
 950 Line 38 changes the state of the object under test, which is supposed to result in a stateChanged(int) signal being emitted. Line 39 checks that the number of signals increases from zero to one. Lines 40 and 41 repeat the process, and again in lines 42 and 43. 
 951 
 952 Line 45 deletes the object under test, and line 47 tests that the destroyed() signal has been emitted. 
 953 
 954 For signals that have arguments (such as our stateChanged(int) signal), you may also wish to check that the arguments were correct. You can do this by looking at the list of signal arguments. Exactly how you do this is fairly flexible, however for simple tests like the one in the example, you can manually work through the list using takeFirst() and check that each argument is correct. This is shown in line 52, 55 and 57 for the first signal. The same approach is shown in lines 59, 61 and 62 for the second signal, and the in lines 64 to 66 for the third signal. For a more complex set of tests, you may wish to apply some data driven techniques. 
 955 
 956 {{Tip|'''Note:''' You should be aware that, for some class implementations, you may need to return control to the event loop to have signals emitted. If you need this, try using the {{qt|QTest}}::qWait() function.}}
 957 
 958 ==Tutorial 6: Integrating with CMake==
 959 
 960 The KDE4 build tool is [http://www.cmake.org CMake], and I assume that you are familiar with the use of CMake. If not, you should review the [[Development/Tutorials/CMake|CMake Tutorial]] first.
 961 
 962 CMake offers quite good support for unit testing, and QTestLib tests can be easily integrated into any CMake build system.
 963 
 964 === Configuring for Testing ===
 965 
 966 Tests are not built by default - you have to enable the test system, and build the tests.
 967 
 968 You enable tests by adding an '''ENABLE_TESTING()''' line to the top of your CMakeLists.txt file.
 969 
 970 In some configurations, there may be a build system option to turn on (or off) the compilation of tests. At this stage, you have to enable the '''BUILD_TESTING''' option in KDE4 modules, however this may go away in the near future, as later version of CMake can build the test applications on demand.
 971 
 972 If the tests are still not building, you might want to issue make buildtests in tests directory.
 973 
 974 === Adding Tests ===
 975 
 976 You add a single test to the list of all tests that can be run by using 
 977 '''ADD_TEST''', which takes a number of arguments:
 978 * The first argument is a display name for the test. This is required.
 979 * The second argument is the name of the executable to be run (including the path to that executable, if required). This is required.
 980 * Any following arguments are passed to the executable. These are optional, and are usually omitted for QTestLib tests.
 981 
 982 Note that '''ADD_TEST''' does nothing if '''ENABLE_TESTING()''' has not been run.
 983 
 984 When choosing a display name, you should adopt a similar convention to the existing tests (e.g. in kdelibs/kdecore, all the display names start with kdecore-, which makes it easy to find the failing test if you run all the tests in kdelibs). If there are no existing tests, using a submodule prefix is probably a good idea.
 985 
 986 The executable doesn't need to be something you build, but it will be for QTestLib, and many other tests. Remember that you need to specify the path, so this normally looks something like "${EXECUTABLE_OUTPUT_PATH}/testapp".
 987 
 988 === KDE4 CMake Recipe for QTestLib ===
 989 
 990 If you are working in a KDE4 environment, then it is pretty easy to get CMake set up to build and run a test on demand.
 991 
 992 <syntaxhighlight lang="text">
 993 set( kwhatevertest_SRCS kwhatevertest.cpp )
 994 
 995 kde4_add_unit_test( kwhatevertest
 996   TESTNAME ksubmodule-kwhatevertest
 997   ${kwhatevertest_SRCS}
 998 )
 999 
1000 target_link_libraries( kwhatevertest
1001   ${KDE4_KDECORE_LIBS}
1002   ${QT_QTTEST_LIBRARY}
1003 )
1004 
1005 </code>
1006 
1007 You are meant to replace "kwhatevertest" with the name of your test application. The target_link_libraries() line will need to contain whatever libraries are needed for the feature you are testing, so if it is a GUI feature, you'll likely need to use "${KDE4_KDEUI_LIBS}.
1008 
1009 === Running the Tests ===
1010 To run all tests, you can just "make test". This will work through each of the tests that have been added (at any lower level) using '''kde4_add_unit_test''', provided that you have '''include(KDE4Defaults)''' in your CMakeLists.txt.
1011 
1012 This is equivalent to running the "ctest" executable with no arguments. If you want finer grained control over which tests are run or the output format, you can use additional arguments. These are explained in the ctest man page ("man ctest" on a *nix system, or run "ctest --help-full").
1013 
1014 To run a single test, use '''./tutorial1.shell''' rather than just '''./tutorial1''', this will make it use the locally-built version of the shared libraries you're testing, rather than the installed ones.
1015 
1016 === Further Reading ===
1017 
1018 Chapter 8 of the [http://www.kitware.com/products/cmakebook.html  CMake Book] provides a detailed description of how to do testing with CMake. Also see Appendix B for more on CTest and the special commands you can use.
1019 
1020 Various sections of the CMake Wiki, especially [http://www.cmake.org/Wiki/CMake_Testing_With_CTest CTest testing]
1021 
1022 ==Tutorial 7: Integrating with qmake==
1023 
1024 TODO
1025 
1026 ==Tutorial 8: XML output==
1027 TODO
1028 
1029 ==Tutorial 9: KDE specifics ==
1030 For KDE specific enhancements see [http://api.kde.org/4.0-api/kdelibs-apidocs/kdecore/html/qtest__kde_8h.html API dox]
1031 
1032 TODO
1033 
1034 == Alternative tools for testing ==
1035 
1036 There are a range of alternative testing approaches that can
1037 be used either with unit tests, or as an addition to the unit tests. 
1038 
1039 ===Static tests===
1040 As noted in the introduction, unit tests are dynamic tests - they exercise the compiled code. Static tests are slightly different - they look for problems in source code, rather than making sure that the object code runs correctly.
1041 
1042 Static test tools tend to identify completely different types of problems to unit tests, and you should seek to use them both. 
1043 
1044 For more information on using static tests, see [[../Code Checking|the Code Checking tutorial]].
1045 
1046 ===Coverage tools and valgrind===
1047 TODO 
1048 
1049 === GUI application testing - Squish and KDExecutor ===
1050 [http://www.froglogic.de/pg?id=Products&category=squish&sub=overview&subsub=overview Squish] by [http://www.froglogic.de froglogic] and [http://www.kdab.net/?page=products&sub=kdexecutor KDExecutor] by [http://www.kdab.net Klarälvdalens Datakonsult (KDAB)] are commercial tools that facilitate GUI testing.
1051 
1052 ==Conclusions==
1053 TODO
1054 
1055 [[Category:KDE4]]