QtのDataDrivenTestでstd::size_tとかを(比較的?)ストレスなく使う


これは

Qt Advent Calendar 201918日目の記事です

昨日は@task_jpさんのQML の一部を別の QML で差し替える方法でした

課題やってたらこんな時間になっちゃった 急いで書かなきゃ(18日23時40分)

さて

みなさんQtのソフトウェアでのTestって何でやってらっしゃるんですかね?
私はとりあえずQt標準のTestで行っています
多くのテストフレームワークと同様にQtのテストフレームワークにもDataDrivenTestの機能が備わっています

公式のサンプルにはこんな例が乗っています

void TestQString::toUpper_data()
{
    QTest::addColumn<QString>("string");
    QTest::addColumn<QString>("result");

    QTest::newRow("all lower") << "hello" << "HELLO";
    QTest::newRow("mixed")     << "Hello" << "HELLO";
    QTest::newRow("all upper") << "HELLO" << "HELLO";
}

void TestQString::toUpper()
{
    QFETCH(QString, string);
    QFETCH(QString, result);

    QCOMPARE(string.toUpper(), result);
}

addColumnテストデータのカラムの型を指定して
newRowでテストデータの値を登録していますね

で、

さっきのサンプルを見ればわかるとおり(?)、
addColumnで登録した型の情報はnewRowの式で参照することができないので、プログラマが間違えるとこんなものも書けてしまいます

void TestQString::toUpper_data()
{
    QTest::addColumn<QString>("string");
    QTest::addColumn<QString>("result");

    QTest::newRow("hoge") << "hello" << 1; // "result"の型がおかしい
}

まあこれくらいならエラーを見てすぐ気づくことができますが、std::size_tを渡そうとしたときで

void TestQString::toUpper_data()
{
    QTest::addColumn<HogeHogeData>("data");
    QTest::addColumn<std::size_t>("size");

    QTest::newRow("hoge") << .... << 1; // 1の型はintになる
}

これは突然expected data of type 'ulong', got 'int' for element 0 of data with tag 'hoge'とか吐いて死ぬことになる

仕方なくすべてのリテラルにsuffixをつけて回るのは非常に面倒だしすぐ忘れる

C++erなら型は気にしないとね!

というわけでこうする

要はnewRowのときに型がわかればいいので

namespace test {

template <class>
struct Column
{
    Column(const char* name) : str(name) {}
    const char* str;
};

template <class... Args>
struct DDTest
{
    DDTest(Column<Args>... cols)
    {
        (QTest::addColumn<Args>(cols.str), ...);
    }
    void newRow(const char* name, Args... args)
    {
        (QTest::newRow(name) << ... << args);
    }
};
template <class... Args>
DDTest(Column<Args>...)->DDTest<Args...>;

} // namespace test
void TestClass::HogeHoge_test_data() {
    test::DDTest dataTest(
        test::Column<std::size_t>("limit"),
        test::Column<std::size_t>("count"));

    dataTest.newRow("10000", 10'000, 1'000'000);
    dataTest.newRow("10000", 20'000, 1'000'000);
    dataTest.newRow("10000", 40'000, 1'000'000);
    dataTest.newRow("10000", 80'000, 1'000'000);
}

rowのラベルとデータが同じカッコ内に並ぶのはちょっと不格好だが、これで型が違うことを防げる(不格好なのはまあどうにでもなる(やってないけど))

ついでに入力中にIDEが型名を表示してくれるので若干助かる?

書いてるうちに日付が変わってしまったZE

明日は

@cod-sushiさんのQtで作ったスクリーンショット作成アプリケーションについて紹介 です