Unit testing

You can perform unit testing for your app. Before you can perform unit testing, you need to create tests for your project in C or C++. You also need to build your project with one of the supported testing frameworks:

You can also perform unit testing with the QTestLib framework using the qtestexample.

The QTestLib framework is a tool for unit testing Qt-based apps and libraries. You can use QTestLib to test individual functions in your app. These tests are compiled into a test binary, which is compiled against the functions that you want to test. Because the tests are compiled into a separate binary, your final product doesn't contain any traces of test functions. For more information, see the QTestLib Manual on the Qt website.

Set up QTestLib

Add CONFIG += qtestlib to your .pro file to compile QTestLib into a test binary. Several macros exist to verify your result, including QVERIFY and QCOMPARE.

Copy cascadestester.hpp and cascadestester.cpp to the src folder of your project. These two files implement the CascadesTester class. The macro QTEST_CASCADES_MAIN implements a main function and registers the CascadesTester class.

#ifndef CASCADESTESTER_HPP
#define CASCADESTESTER_HPP

#include <bb/cascades/Application>
#include <bb/cascades/Page>

#include <QtCore/QObject>
#include <QtTest/QtTest>

/**
 * @short A helper class to execute QTestLib based unit tests in Cascades
 *
 * This class executes a QTestLib based unit test inside the main event loop
 * of Cascades, so that Cascades controls can be tested.
 */
class CascadesTester : public QObject
{
    Q_OBJECT

    public:
        /**
         * Creates a new cascades tester object.
         *
         * @param object The unit test object.
         * @param argc The count of the command line arguments.
         * @param argv The command line arguments.
         */
        explicit CascadesTester(QObject *object, int argc = 0, char **argv = 0);

    public Q_SLOTS:
        /**
         * Starts the execution of the unit test.
         */
        void startTest();

    private:
        QObject *m_object;
        int m_argc;
        char **m_argv;
};

#define QTEST_CASCADES_MAIN(TestObject) \
int main(int argc, char **argv) \
{ \
    bb::cascades::Application app(argc, argv); \
    TestObject tc; \
    CascadesTester tester(&tc, argc, argv); \
    bb::cascades::Application::instance()->setScene(new bb::cascades::Page()); \
    QMetaObject::invokeMethod(&tester, "startTest", Qt::QueuedConnection); \
    return bb::cascades::Application::exec(); \
}

#endif
#include "cascadestester.hpp"

#include <bb/cascades/Application>

#include <QtTest/QTest>

CascadesTester::CascadesTester(QObject *object, int argc, char **argv)
    : m_object(object), m_argc(argc), m_argv(argv)
{

}

void CascadesTester::startTest()
{
    // Start the unit test
    const int result = QTest::qExec(m_object, m_argc, m_argv);

    // Terminate application
    bb::cascades::Application::exit(result);
}

Create tests

You can define a class for your test by subclassing QObject and adding private slots to it. Each private slot is a test function in your test. You can use QTest::qExec() to run all test functions in your class. Here's an example of how to create a class for your tests. The following example is created in a TestMyObject.hpp file:

#include <QObject>

class TestMyObject: public QObject
{
    Q_OBJECT

    private slots:
        void test1();
        void test2();
    private:
        MyObject* p;
};

Then you can create tests in a TestMyObject.cpp file. For example:

#include "testmyobject.hpp"

#include <QtTest/QtTest>

void TestMyObject::test1()
 {
     p->doSomething();
     Q_VERIFY(someConditionMet());
  }

In your main.cpp file, you can add the following line:

#include "cascadestester.hpp"
#include "testmyobject.hpp"

// The macro QTEST_CASCADES_MAIN implements a main 
// function and registers the CascadesTester class.

QTEST_CASCADES_MAIN(TestMyObject)
.

Data-driven unit testing

When you are testing a function, it might be useful to test it with different arguments. You can do this testing with calls to QCOMPARE. For an example, see the FilterProxyDataModelTest sample in GitHub.

Or you can use QTestLib and data-driven testing. For example, one function is the actual test case and another function, with the suffix _data, creates the data set. The test function is invoked for each row in the data set.

In the _data function, you set up your data using QTest::addColumn() and create test data using QTest::newRow(). For example:

QTest::addColumn<QString>( "input" );
QTest::addColumn<QString>( "result" );

QTest::newRow( "all lower" ) << "hello" << "hello";
QTest::newRow( "mixed" ) << "HellO" << "hello";

In the test case, you get the data using the QFETCH macro:

QFETCH( QString, input );
QFETCH( QString, result );
QCOMPARE( input.toLower(), result );

You can use your own data types if they have been registered using Q_DECLARE_METATYPE.

You can set up the macro QEXPECT_FAIL to expect failure from a given piece of data in the set. In the following code sample, the first parameter is the name of the data value, the second parameter is a message to display, and the third parameter tells the app whether the test case should continue after an error.

QFETCH( QString, data );
QFETCH( int, result );
QEXPECT_FAIL("expected error",
             "To be fixed", 
              Abort );
QCOMPARE( data.count(), result );

The QObject emits a signal in response. The class QSignalSpy is designed to verify emitted signals. The class inherits QList < QList < QVariant > >. The outer list's items each represent a signal emission, while the inner list's items represent the signal's arguments.

Create benchmarks

Test functions can be benchmarked.

  1. Apply the QBENCHMARK macro to parts of test functions. There can be at most one QBENCHMARK for each test function.
  2. Run the test case in the usual way.

For example:

class MyFirstBenchmark: public QObject {
    Q_OBJECT
private slots:
    void myFirstBenchmark() {
        QString string1, string2;
        QBENCHMARK {
            string1.localeAwareCompare(string2);
        }
    }
};

Use backends

Backends measure and report the performance of benchmarks. These backends are selected using command-line options:

  • The default walltime backend measures and reports elapsed time.
  • The -tickcounter option selects the CPU tick counter backend.
  • The -eventcounter option selects the event counter backend.
  • The -callgrind option selects the Callgrind backend, which is specific to Linux.

Some backends run benchmarked code multiple times to ensure accurate timings.

For more details, see the QTestLib Manual on the Qt website.

Launch unit tests

  1. On the Momentics IDE toolbar, in the Launch configuration drop-down list, click Create New Configuration.
  2. On the Initial Launch Mode screen, click Run, and then click Next.
  3. On the Launch Configuration Type screen, click BlackBerry C/C++ Application Unit Test, and then click Next.
  4. On the Launch Configuration Properties screen, click the C/C++ Testing tab.
  5. In the Tests Runner drop-down list, click Qt Tests Runner, and then click Finish.
  6. On the toolbar, in the Launch target drop-down list, click the device or simulator to test with.
  7. In the Launch mode drop-down list, click Run, and then click Momentics launch button.

Last modified: 2015-03-31



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus