- Qt 6.9
- Qt Test
- Qt Test Tutorial
- Chapter 2: Data Driven Testing
- Qt 6.9.2 Reference Documentation

Chapter 1: Writing a Unit Test ◦ Chapter 3: Simulating GUI Events

# Chapter 2: Data Driven Testing

This chapter demonstrates how to execute a test multiple times with different test data.

So far, we have hard coded the data we wanted to test into our test function. If we add more test data, the function might look like this:

     QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
     QCOMPARE(QString("Hello").toUpper(), QString("HELLO"));
     QCOMPARE(QString("HellO").toUpper(), QString("HELLO"));
     QCOMPARE(QString("HELLO").toUpper(), QString("HELLO"));

To prevent the function from being cluttered with repetitive code, Qt Test supports adding test data to a test function. All we need is to add another private slot to our test class:

     class TestQString: public QObject
     {
         Q_OBJECT

     private slots:
         void toUpper_data();
         void toUpper();
     };

## Writing the Data Function

A test function's associated data function has `_data` appended to its name. Our data function looks like this:

     void TestQString::toUpper_data()
     {
         QTest::addColumn<QString>("string");
         QTest::addColumn<QString>("result");

         QTest::newRow("all-lower") << "hello" << "HELLO";
         QTest::newRow("mixed")     << "Hello" << "HELLO";
         QTest::newRow("all-upper") << "HELLO" << "HELLO";
     }

First, we define the two elements of our test table using the QTest::addColumn() function: a test string and the expected result of applying the QString::toUpper() function to that string.

Then, we add some data to the table using the QTest::newRow() function. We can also use QTest::addRow() if we need to format some data in the row name, for example when generating many data rows iteratively. Each row of data will become a separate row in the test table.

QTest::newRow() takes one argument: a name that will be associated with the data set and used in the test log to identify the data row. QTest::addRow() takes a (`printf`-style) format string followed by the parameters to be represented in place of the formatting tokens in the format string. Then, we stream the data set into the new table row. First an arbitrary string, and then the expected result of applying the QString::toUpper() function to that string.

You can think of the test data as a two-dimensional table. In our case, it has two columns called `string` and `result` and three rows. In addition, a name and an index are associated with each row:

index | name | string | result
--- | --- | --- | ---
0 | all-lower | "hello" | HELLO
1 | mixed | "Hello" | HELLO
2 | all-upper | "HELLO" | HELLO

When data is streamed into the row, each datum is asserted to match the type of the column whose value it supplies. If any assertion fails, the test is aborted.

The names of rows and columns, in a given test function's data table, should be unique: if two rows share a name, or two columns share a name, a warning will (since Qt 6.5) be produced. See qWarning() for how you can cause warnings to be treated as errors and Test for Warnings for how to get your tests clear of other warnings.

## Rewriting the Test Function

Our test function can now be rewritten:

     void TestQString::toUpper()
     {
         QFETCH(QString, string);
         QFETCH(QString, result);

         QCOMPARE(string.toUpper(), result);
     }

The TestQString::toUpper() function will be executed three times, once for each entry in the test table that we created in the associated TestQString::toUpper_data() function.

First, we fetch the two elements of the data set using the QFETCH() macro. QFETCH() takes two arguments: The data type of the element and the element name. Then, we perform the test using the QCOMPARE() macro.

This approach makes it very easy to add new data to the test without modifying the test itself.

## Preparing the Stand-Alone Executable

And again, to make our test case a stand-alone executable, the following two lines are needed:

     QTEST_MAIN(TestQString)
     #include "testqstring.moc"

As before, the QTEST_MAIN() macro expands to a simple main() method that runs all the test functions, and since both the declaration and the implementation of our test class are in a .cpp file, we also need to include the generated moc file to make Qt's introspection work.

## Building the Executable

You can build the test case executable using CMake or qmake.

### Building with CMake

Configure your build settings in your CMakeLists.txt file:

     # Copyright (C) 2022 The Qt Company Ltd.
     # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

     cmake_minimum_required(VERSION 3.16)
     project(tutorial2 LANGUAGES CXX)

     find_package(Qt6 REQUIRED COMPONENTS Core Gui Test Widgets)

     qt_standard_project_setup()

     qt_add_executable(tutorial2
         testqstring.cpp
     )

     set_target_properties(tutorial2 PROPERTIES
         WIN32_EXECUTABLE TRUE
         MACOSX_BUNDLE TRUE
     )

     target_link_libraries(tutorial2 PRIVATE
         Qt6::Core
         Qt6::Gui
         Qt6::Test
         Qt6::Widgets
     )

     install(TARGETS tutorial2
         BUNDLE  DESTINATION .
         RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
         LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     )

     qt_generate_deploy_app_script(
         TARGET tutorial2
         OUTPUT_SCRIPT deploy_script
         NO_UNSUPPORTED_PLATFORM_ERROR
     )
     install(SCRIPT ${deploy_script})

Next, from the command line, run either `cmake` or use the `qt-cmake` convenience script located in `Qt-prefix/<version>/<platform>/bin/qt-cmake`:

     <Qt-prefix>/<version>/<platform>/bin/qt-cmake <source-dir> <build-dir> -G Ninja

Then, run your preferred generator tool to build the executable. Here, we're using Ninja:

     ninja

### Building with qmake

Configure your build settings in your `.pro` file:

     QT += widgets testlib

     SOURCES = testqstring.cpp

     # install
     target.path = $$[QT_INSTALL_EXAMPLES]/qtestlib/tutorial2
     INSTALLS += target

Next, run `qmake`, and, finally, run `make` to build your executable:

     qmake
     make

## Running the Executable

Running the resulting executable should give you the following output:

     ********* Start testing of TestQString *********
     Config: Using QtTest library %VERSION%, Qt %VERSION%
     PASS   : TestQString::initTestCase()
     PASS   : TestQString::toUpper(all-lower)
     PASS   : TestQString::toUpper(mixed)
     PASS   : TestQString::toUpper(all-upper)
     PASS   : TestQString::cleanupTestCase()
     Totals: 5 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms
     ********* Finished testing of TestQString *********