第2章 データ駆動型テスト

この章では、異なるテストデータを使ってテストを複数回実行する方法を示します。

これまでのところ、テストしたいデータをテスト関数にハードコーディングしてきました。さらにテストデータを追加すると、関数はこのようになります:

QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
QCOMPARE(QString("Hello").toUpper(), QString("HELLO"));
QCOMPARE(QString("HellO").toUpper(), QString("HELLO"));
QCOMPARE(QString("HELLO").toUpper(), QString("HELLO"));

繰り返しのコードで関数が乱雑になるのを防ぐために、Qt Test はテスト関数にテストデータを追加することをサポートしています。必要なのは、テストクラスにもう一つプライベートスロットを追加することだけです:

class TestQString: public QObject
{
    Q_OBJECT

private slots:
    void toUpper_data();
    void toUpper();
};

データ関数を書く

テスト関数に関連するデータ関数は、その名前に_data が付加されます。私たちのデータ関数はこのようになります:

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

まず、QTest::addColumn ()関数を使って、テストテーブルの2つの要素を定義します。テスト文字列と、その文字列にQString::toUpper ()関数を適用した場合の期待される結果です。

次に、QTest::newRow ()関数を使用してテーブルにデータを追加します。また、多くのデータ行を繰り返し生成する場合など、行名に何らかのデータの書式を設定する必要がある場合にも、QTest::addRow ()を使用することができます。データの各行は、テスト・テーブルの個別の行になります。

QTest::newRow() は1つの引数を取ります:データセットに関連付けられ、データ行を識別するためにテストログで使用される名前です。QTest::addRow() は、(printf-style)書式文字列の後に、書式文字列の書式トークンの代わりに表現されるパラメータを取ります。次に、データセットを新しいテーブル行にストリームする。最初に任意の文字列、次にその文字列にQString::toUpper()関数を適用した結果の期待値。

テスト・データは2次元のテーブルと考えることができる。この場合、stringresult という2つの列と3つの行がある。また、各行には名前とインデックスが関連付けられている:

インデックス名前文字列結果
0全下位"hello"ハロー
1ミックス「ハローこんにちは
2オール・アッパー「ハローHELLO

データが行にストリームされると、各データムは、その値が供給される列の型と一致するようにアサートされる。アサーションが失敗した場合、テストは中断される。

2つの行が同じ名前であったり、2つの列が同じ名前であったりすると、(Qt 6.5以降)警告が表示されます。警告をエラーとして扱う方法についてはqWarning() を、他の警告からテストをクリアする方法についてはTest for Warningsを参照してください。

テスト関数の書き換え

テスト関数を書き直すことができます:

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

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

TestQString::toUpper() 関数は、関連する TestQString::toUpper_data() 関数で作成したテストテーブルの各エントリに対して 1 回ずつ、計 3 回実行されます。

最初に、QFETCH() マクロを使用して、データセットの2つの要素を取得します。QFETCH() は2つの引数を取ります:要素のデータ型と要素名です。次に、QCOMPARE() マクロを使用してテストを実行します。

この方法により、テスト自体に変更を加えることなく、新しいデータをテストに追加することが非常に簡単になります。

スタンドアロン実行ファイルの準備

テストケースをスタンドアロン実行ファイルにするには、次の2行が必要です:

QTEST_MAIN(TestQString)
#include "testqstring.moc"

前回と同様、QTEST_MAIN() マクロは、すべてのテスト関数を実行する単純な main() メソッドに展開されます。テストクラスの宣言と実装の両方が .cpp ファイルにあるので、Qt のイントロスペクションを動作させるために、生成された moc ファイルもインクルードする必要があります。

実行ファイルのビルド

CMakeまたはqmakeを使ってテストケースの実行ファイルをビルドします。

CMakeでビルドする

CMakeLists.txt ファイルでビルド設定を行います:

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

cmake_minimum_required(VERSION 3.16)
project(tutorial2 LANGUAGES CXX)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Test Widgets)

qt_standard_project_setup()

qt_add_executable(tutorial2
    testqstring.cpp
)

set_target_properties(tutorial2 PROPERTIES
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)

target_link_libraries(tutorial2 PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Test
    Qt6::Widgets
)

install(TARGETS tutorial2
    BUNDLE  DESTINATION .
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

qt_generate_deploy_app_script(
    TARGET tutorial2
    OUTPUT_SCRIPT deploy_script
    NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})

次に、コマンドラインからcmake を実行するか、Qt-prefix/<version>/<platform>/bin/qt-cmake にあるqt-cmake 便利スクリプトを使用します:

<Qt-prefix>/<version>/<platform>/bin/qt-cmake <source-dir> <build-dir> -G Ninja

次に、お好みのジェネレーター・ツールを実行して実行ファイルをビルドします。ここではNinjaを使っています:

ninja

qmakeでビルドする

.pro ファイルでビルド設定を行います:

QT += widgets testlib

SOURCES = testqstring.cpp

# install
target.path = $$[QT_INSTALL_EXAMPLES]/qtestlib/tutorial2
INSTALLS += target

次にqmake を実行し、最後にmake を実行して実行ファイルをビルドします:

qmake
make

実行ファイルの実行

出来上がった実行ファイルを実行すると、以下のような出力が得られるはずだ:

********* Start testing of TestQString *********
Config: Using QtTest library %VERSION%, Qt %VERSION%
PASS   : TestQString::initTestCase()
PASS   : TestQString::toUpper(all-lower)
PASS   : TestQString::toUpper(mixed)
PASS   : TestQString::toUpper(all-upper)
PASS   : TestQString::cleanupTestCase()
Totals: 5 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms
********* Finished testing of TestQString *********

本書に含まれる文書の著作権はそれぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。