| Writing Unittests for Qt4 and KDE4 with QtTestLib | ||
|---|---|---|
| <<< Previous | Next >>> | |
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.
The easiest way to understand data driven testing is by an example, as shown below:
Example 9. QDate test code, data driven version
1 #include <QtTest>
2 #include <QtCore>
3 class testDate: public QObject
4 {
5 Q_OBJECT
6 private slots:
7 void testValidity();
8 void testMonth_data();
9 void testMonth();
10 };
11 void testDate::testValidity()
12 {
13 // 12 March 1967
14 QDate date( 1967, 3, 12 );
15 QVERIFY( date.isValid() );
16 }
17 void testDate::testMonth_data()
18 {
19 QTest::addColumn<int>("year"); // the year we are testing
20 QTest::addColumn<int>("month"); // the month we are testing
21 QTest::addColumn<int>("day"); // the day we are testing
22 QTest::addColumn<QString>("monthName"); // the name of the month
23 QTest::newRow("1967/3/11") << 1967 << 3 << 11 << QString("March");
24 QTest::newRow("1966/1/10") << 1966 << 1 << 10 << QString("January");
25 QTest::newRow("1999/9/19") << 1999 << 9 << 19 << QString("September");
26 // more rows of dates can go in here...
27 }
28 void testDate::testMonth()
29 {
30 QFETCH(int, year);
31 QFETCH(int, month);
32 QFETCH(int, day);
33 QFETCH(QString, monthName);
34 QDate date;
35 date.setYMD( year, month, day);
36 QCOMPARE( date.month(), month );
37 QCOMPARE( QDate::longMonthName(date.month()), monthName );
38 }
39 QTEST_MAIN(testDate)
40 #include "tutorial2.moc"
|
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.
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.
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.
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:
Example 10. Results of data driven testing, showing QFETCH
$ ./tutorial2 -v2
********* Start testing of testDate *********
Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051020
INFO : testDate::initTestCase() entering
PASS : testDate::initTestCase()
INFO : testDate::testValidity() entering
INFO : testDate::testValidity() QVERIFY(date.isValid())
Loc: [tutorial2.cpp(19)]
PASS : testDate::testValidity()
INFO : testDate::testMonth() entering
INFO : testDate::testMonth(1967/3/11) COMPARE()
Loc: [tutorial2.cpp(44)]
INFO : testDate::testMonth(1967/3/11) COMPARE()
Loc: [tutorial2.cpp(45)]
INFO : testDate::testMonth(1966/1/10) COMPARE()
Loc: [tutorial2.cpp(44)]
INFO : testDate::testMonth(1966/1/10) COMPARE()
Loc: [tutorial2.cpp(45)]
INFO : testDate::testMonth(1999/9/19) COMPARE()
Loc: [tutorial2.cpp(44)]
INFO : testDate::testMonth(1999/9/19) COMPARE()
Loc: [tutorial2.cpp(45)]
PASS : testDate::testMonth()
INFO : testDate::cleanupTestCase() entering
PASS : testDate::cleanupTestCase()
Totals: 4 passed, 0 failed, 0 skipped
********* Finished testing of testDate *********
|
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.
Example 11. QDate test code, data driven version using QTEST
void testDate::testMonth()
{
QFETCH(int, year);
QFETCH(int, month);
QFETCH(int, day);
QDate date;
date.setYMD( year, month, day);
QCOMPARE( date.month(), month );
QTEST( QDate::longMonthName(date.month()), "monthName" );
}
|
In the example above, note that monthname is enclosed in quotes, and we no longer have a QFETCH call for monthname.
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.
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 ./tutorial2 -v2 testMonth:1967/3/11. The result of this is shown below.
Example 12. QDate unit test output - selected function and data
$ ./tutorial2 -v2 testMonth:1967/3/11 ********* Start testing of testDate ********* Config: Using QTest library 4.1.0, Qt 4.1.0-snapshot-20051020 INFO : testDate::initTestCase() entering PASS : testDate::initTestCase() INFO : testDate::testMonth() entering INFO : testDate::testMonth(1967/3/11) COMPARE() Loc: [tutorial2.cpp(44)] INFO : testDate::testMonth(1967/3/11) COMPARE() Loc: [tutorial2.cpp(45)] PASS : testDate::testMonth() INFO : testDate::cleanupTestCase() entering PASS : testDate::cleanupTestCase() Totals: 3 passed, 0 failed, 0 skipped ********* Finished testing of testDate ********* |
| <<< Previous | Home | Next >>> |
| Tutorial 1 - A simple test of a date class | Tutorial 3 - Testing Graphical User Interfaces |