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.
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;
}