フェルマータ

個人用のメモ。ソフトウェアの導入とかが多くなる予定。

Qt で複数のテストを同時に実行する。

経緯

 Qt には QTest というテスト用ライブラリがついてくる。

が、しかしデフォルトだと1プロジェクトに対して1テストだけしか実行できない。 Jenkins に Qt 用のプラグインあるので別に CI していれば関係なくね、とも言えるが手元でテストするのにあまり優しくない。結局手元で実行するならテストはまとまっていて一回で済んだ方が嬉しいので、一つにまとめる方法があればそれにこしたことはない。

やり方

 そんなわけでややこしい Qt のテストライブラリだが、頑張ればまとめることができる。結局テスト用の QObject の private slots を叩きまくるだけなので、 QObject の列がありさえすればテストの実行はできる。そこでこんなソースコードを用意する。

#ifndef AUTOTEST_H
#define AUTOTEST_H

#include <QTest>
#include <QHash>
#include <QString>
#include <QSharedPointer>

namespace QAutoTest
{
QHash<QString, QObject *> &tests();

int run(int argc, char *argv[]);
}

template <class T> class Test
{
public:
    QSharedPointer<T> test;
    Test(const QString &name) : test(new T)
    {
        if(!QAutoTest::tests().contains(name))
        {
            QAutoTest::tests()[name] = test.data();
        }
    }
};

#define DECLARE_TEST(className) static Test<className> t(#className);

#endif // AUTOTEST_H

AutoTest.h

#include "AutoTest.h"

QHash<QString, QObject *> &QAutoTest::tests()
{
    static QHash<QString, QObject *> t;
    return t;
}

int QAutoTest::run(int argc, char *argv[])
{
    int ret = 0;
    QList<QString> failureTests;
    foreach(QObject* test, tests().values())
    {
        int result =  QTest::qExec(test, argc, argv);
        if(result)
        {
            failureTests.append(tests().key(test));
        }
        ret |= result;
    }
    if(ret == 0)
    {
        printf("[success] All test succeeded!\n");
    }
    else
    {
        printf("[error] %d test(s) failed.\n", failureTests.size());
        foreach(const QString &testName, failureTests)
        {
            printf("[error]  Test failed in: %s\n", testName.toLocal8Bit().data());
        }
    }
    return ret;
}

AutoTest.cpp

#include "AutoTest.h"

int main(int argc, char *argv[])
{
    return QAutoTest::run(argc, argv);
}

main.cpp

後はテストしたいクラスを適当に作って DECLARE_TEST でクラス名を与えればいい。以下の様な感じ。

#ifndef __SAMPLE_TEST__
#define __SAMPLE_TEST__

#include <QTest.h>
#include "AutoTest.h"

class SampleTest : public QObject
{
    Q_OBJECT
private slots:
    void test()
    {
        // do your tests here!
    }
}

DECLARE_TEST(SampleTest)

#endif // __SAMPLE_TEST

SampleTest.h

これで複数のテストを同時に実行できる。とても便利。