Thursday, November 08, 2007

Auto test stub generator

When writing auto tests for Qt code one of the more annoying parts of the task is getting the initial test file up and running. The basic makeup of the test files are about the same so I wrote a little tool that helps by automatically creating stub code for the class you are writing a test for. Then it is just a task of going through the file filling it in with the actual tests.

Some features include:
- Creates a stub tests for each non private function in the class.
- Creates a subclass for the class to expose any protected functions for testing.
- Creates a basic sanity test that just calls each function.
- Creates a _data() function for each test.
- Populates the _data() functions with columns for each argument and the return value.
- Adds QFETCH stub code that matches the generated _data function as a place to start from.
- Adds the four init and cleanup functions with documentation so you don't have to look up what does what.
- Adds signal spys to each tests if the class contains any signals.

The source code can be found: QAutoTestGenerator

Here is an example class:

class Example : QObject
{
Q_OBJECT
signals:
void done();
public:
void launch(int value);
protected:
bool crashed();
};

And the generated stub:

#include <QtTest/QtTest>
#include <example.h>

class tst_Example : public QObject
{
Q_OBJECT

public slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();

private slots:
void example_data();
void example();
void launch_data();
void launch();
void crashed_data();
void crashed();
void done_data();
void done();
};

// Subclass that exposes the protected functions.
class SubExample : public Example
{
public:
bool call_crashed()
{ return SubExample::crashed(); }

void call_done()
{ return SubExample::done(); }
};

// This will be called before the first test function is executed.
// It is only called once.
void tst_Example::initTestCase()
{
}

// This will be called after the last test function is executed.
// It is only called once.
void tst_Example::cleanupTestCase()
{
}

// This will be called before each test function is executed.
void tst_Example::init()
{
}

// This will be called after every test function.
void tst_Example::cleanup()
{
}

void tst_Example::example_data()
{
}

void tst_Example::example()
{
SubExample example;
/*
example.isDone();
example.launch();
example.call_crashed();
example.call_done();
*/
QVERIFY(false); // remove after test is implemented
}

void tst_Example::launch_data()
{
QTest::addColumn<int>("value");
QTest::newRow("null") << 0;
}

// public void launch(int value)
void tst_Example::launch()
{
/*
QFETCH(int, value);

SubExample example;

QSignalSpy spy0(&example, SIGNAL(done()));
QCOMPARE(spy0.count(), 0);
example.launch();
*/
QVERIFY(false); // remove after test is implemented
}

void tst_Example::crashed_data()
{
/*
QTest::addColumn<bool>("crashed");
QTest::newRow("null") << 0;
*/
}

// protected bool crashed()
void tst_Example::crashed()
{
/*
QFETCH(bool, crashed);

SubExample example;

QSignalSpy spy0(&example, SIGNAL(done()));
QCOMPARE(spy0.count(), 0);
example.call_crashed();
*/
QVERIFY(false); // remove after test is implemented
}

void tst_Example::done_data()
{
QTest::addColumn<int>("foo");
QTest::newRow("null") << 0;
}

// protected void done()
void tst_Example::done()
{
/*
QFETCH(int, foo);

SubExample example;

QSignalSpy spy0(&example, SIGNAL(done()));
QCOMPARE(spy0.count(), 0);
example.call_done();
*/
QVERIFY(false); // remove after test is implemented
}

QTEST_MAIN(tst_Example)
#include "tst_example.moc"

6 comments:

Andreas Ramm said...

That's way cool.

This takes unit testing with Qt from "really easy" to "I love doing this".

One minor gripe would be the missing 'e' in the generated tst classes. :)

Overall fantastic work. Thanks for sharing it.

Benjamin Meyer said...

The "tst" is historical I guess you could say. All of the tests for Qt use it so I just copied it.

Ilkka said...

This looks like a pretty handy tool, for sure. Now if only I could find an easy way to build and run a whole bunch of QTest unit tests in a single directory...

Benjamin Meyer said...

@ilkka: Why can't you just write a makefile to build all of the tests and run them in a directory?

Ilkka said...

@benjamin: because that's what qmake is for :) But as it happens, just today I discovered that if one creates a qmake .pro file with TEMPLATE=subdirs, one can then set SUBDIRS to a list of .pro files in the same directory... this method combined with clever includes lets me easily do what I want, and qmake does all the heavy lifting for me.

Ilkka said...

...reading my comment again I see that I should start a software engineering blog myself, for topics that perhaps demand a bit more verbosity than a mere couple of sentences :)

Popular Posts