qtdata-driven-testsqtest

Data re-driven testing in Qt


Can I get QTest to use a single data-driving set more than once?


We using Qt's QTest framework for the low-level tests in a project, and using data-driven style where it makes sense.

I've come to a point where I want to do several distinct sets of tests on a single set of test data. The obvious design is

void testcalss::teststuff_data()
{
    QTest::addColumn<int>("number");
    QTest::addColumn<double>("size");

    QTest::newRow("first") << 1 << 3.14;
    QTest::newRow("second") << 42 << 2.718;
}

void testclass::teststuff()
{
    QFETCH(int, number);
    QFETCH(double, value);

    // Test one thing about this stuff
    QCOMPARE( f(number), value );      // <=== If it fails here ...

    // test some other thing
    QCOMPARE( g(number), h(value));    // <=== ... it doesn't even try this
}

More or less right out of the documentation, right?

But a small annoyance is the way it shortcuts subsequent tests if an early one fails (notes in the comments). Sometimes this is a desired behavior and sometimes not, but just now I want it to try those second test even if the first one fails.

A possible alternate implementation scheme is

void testcalss::teststuff_data()
{
    QTest::addColumn<int>("number");
    QTest::addColumn<double>("size");

    QTest::newRow("first") << 1 << 3.14;
    QTest::newRow("second") << 42 << 2.718;
}

void testclass::teststuff()
{
    QFETCH(int, number);
    QFETCH(double, value);

    QCOMPARE( f(number), value );      
}

void testclass::teststuff2()
{
    QFETCH(int, number);              // <=== No data!
    QFETCH(double, value);

    QCOMPARE( g(number), h(value));    

}

but, of course, the input from teststuff_data() has already been consumed by the time teststuff2 starts.


Solution

  • This trick is to re-invoke the previous code in a context that Qt recognizes as setting up some data. A new _data method that just calls the old one does not require any code duplication.

    void testcalss::teststuff_data()
    {
        QTest::addColumn<int>("number");
        QTest::addColumn<double>("size");
    
        QTest::newRow("first") << 1 << 3.14;
        QTest::newRow("second") << 42 << 2.718;
    }
    
    void testclass::teststuff()
    {
        QFETCH(int, number);
        QFETCH(double, value);
        QCOMPARE( f(number), value );
    }
    
    void testclass::teststuff2_data()    // <=== define a _data method that ...
    {
        teststuff_data();                // <=== ... re-runs the previous set-up
    }
    
    void testclass::teststuff2()
    {
        QFETCH(int, number);
        QFETCH(double, value);
    
        QCOMPARE( g(number), h(value));
    }
    

    As usual the test framework is sensitive to the order of method declaration so the header should look like

    class testclass: public QObject
    {
       Q_OBJECT
    
    // c'tor and what not ...
    
    private slots:
       void teststuff_data();
       void teststuff;
    
       void teststuff2_data();
       void teststuff2;
    }