QTのQtestlibモジュールでのテスト
-
02-10-2019 - |
質問
QTのユニットテストシステムでいくつかのテストを書き始めました。
通常、テストをどのように整理しますか? 1つのモジュールクラスにつき1つのテストクラスです。または、モジュール全体を単一のテストクラスでテストしますか? QTドキュメントは、以前の戦略に従うことを示唆しています。
モジュールのテストを書きたいです。モジュールは、モジュールユーザーが使用するクラスは1つだけ提供されますが、他のクラスには多くのロジックが抽象化されています。これは、パブリッククラスのテスト以外にテストしたいと思います。
問題は、QTが提案したテストを実行する方法を含むことです QTEST_MAIN
大きい:
QTEST_MAIN(TestClass)
#include "test_class.moc"
そして最終的に、1つのテストプログラムは、1つのテストクラスのみをテストできます。そして、モジュール内のすべてのクラスのテストプロジェクトを作成するのはちょっと嫌です。
もちろん、 QTEST_MAIN
マクロ、書き直し、他のテストクラスを実行します。しかし、箱から出して作る何かがありますか?
これまでのところ、私は手作業でそれをします:
#include "one.h"
#include "two.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
TestOne one;
QTest::qExec(&one, argc, argv);
TestOne two;
QTest::qExec(&two, argc, argv);
}
解決
ええ、QTESTはビット奇妙なテスト構造を強制し、一般にGoogleテスト/モックフレームワークよりも劣っています。 1つのプロジェクトでは、QTEST(クライアント要件)を使用することを余儀なくされています。これが私がそれを使用する方法です。
- すべてのテストをサブディールテンプレートプロジェクトとしてコンパイルします
- 新しいテストの作成を容易にするために、common.priファイルを使用して多くのプロジェクト構成を共有します。すべてのテスト.proファイルに含めます。
- 可能であれば、オブジェクトファイルディレクトリを共有してコンパイルを高速化します
- バッチ+awk+SEDスクリプトを使用してすべてを実行します。
この4つのポイントを設定するのは非常に簡単で、Qtestの使用がほとんど快適になります。上記の構成によって解決されない複数のテストを実行することにいくつかの問題がありますか?
PS:ランニングテストはあなたがそうするようにテストします。つまり、複数のQTEST :: QEXECを呼び出すと、-Oコマンドラインスイッチで問題が発生します - 最後のテストクラスの結果のみが得られます。
他のヒント
@cjhuittの投稿者に関連しています
これは、各テストオブジェクトを手動で呼び出す必要性を削除する例です
私はこのようなことを避けようとします:
MyTestClass1 t1; t1.run();
MyTestClass2 t2; t2.run();
//etc...
私の解決策は、テストオブジェクトを静的リストに追加する基本クラスから継承できるようにすることですメインプログラムは、そのリスト内のすべてのテストオブジェクトを実行します。そのようにして、サポートフレームワークコードのいずれも変更する必要はありません。変更されるのは、テストクラス自体だけです。
これが私がそれをする方法です:
qtestsuite.h - テストオブジェクトのベースクラス
#ifndef QTESTSUITE_H
#define QTESTSUITE_H
#include <QObject>
#include <vector>
class QTestSuite : public QObject
{
Q_OBJECT
public:
static std::vector<QObject*> m_suites;
public:
explicit QTestSuite();
};
#endif // QTESTSUITE_H
qtestsuite.cpp
#include "qtestsuite.h"
#include <iostream>
std::vector<QObject*> QTestSuite::m_suites;
QTestSuite::QTestSuite() : QObject()
{
m_suites.push_back(this);
}
testall.cpp - テストを実行します
#include "qtestsuite.h"
#include <QtTest/QtTest>
#include <iostream>
int main(int, char**)
{
int failedSuitesCount = 0;
std::vector<QObject*>::iterator iSuite;
for (iSuite = QTestSuite::m_suites.begin(); iSuite != QTestSuite::m_suites.end(); iSuite++)
{
int result = QTest::qExec(*iSuite);
if (result != 0)
{
failedSuitesCount++;
}
}
return failedSuitesCount;
}
mytestsuite1.cpp - 例のテストオブジェクト、これらをさらに作成します
#include "qtestsuite.h"
#include <QtTest/QtTest>
class MyTestSuite1: public QTestSuite
{
Q_OBJECT
private slots:
void aTestFunction();
void anotherTestFunction();
};
void MyTestSuite1::aTestFunction()
{
QString str = "Hello";
QVERIFY(str.toUpper() == "this will fail");
}
void MyTestSuite1::anotherTestFunction()
{
QString str = "Goodbye";
QVERIFY(str.toUpper() == "GOODBYE");
}
static MyTestSuite1 instance; //This is where this particular test is instantiated, and thus added to the static list of test suites
#include "mytestsuite1.moc"
また、.proファイルを作成します
qmake -project "CONFIG += qtestlib"
QTESTを使用したセットアップでは、いくつかのことを行い、それを改善しました。
- 新しいユニットテストクラスの基本クラスとして使用されるQObjectのサブクラスを定義します。
- そのクラスのコンストラクターでは、テストのインスタンスをテストの静的リストに追加し、デストラクタで削除します。
- 次に、テストをループして使用して実行する静的関数があります
QTest::qExec()
. 。 (毎回返される値を蓄積し、関数からそれを返します。) main()
この関数を呼び出し、結果を成功/失敗として返します。- 最後に、特定のテスト自体のコンパイルユニットに、通常、そのクラスの静的インスタンスを含めます。
このセットアップは、クラスが以前にインスタンス化されることを意味します main()
実行されるので、メインが実行されたときにテストするためにクラスのリストに追加されます。フレームワークでは、クラスを適切に継承するだけで、常に実行したい場合は静的インスタンスをインスタンスする必要があります。
また、コマンドラインスイッチに基づいて追加される他のオプションのテストを作成することもあります。
通常、テスト中のクラスごとに1つのテスト実行可能ファイルを使用してテストを整理します。
そして最終的に、1つのテストプログラムは、1つのテストクラスのみをテストできます。
これは良いことです。テストを互いに分離し、1つのテストでのクラッシュなどのものが他のすべてのテストをブロックするのを防ぎます。そのクラッシュは、テスト中のいくつかのクラスの共通コンポーネントによって引き起こされる可能性があります。障害のパターンは、問題の根本的な起源にあなたをひっくり返します。基本的に、テストが互いに独立している場合、障害の診断情報がより良い診断情報があります。
複数の実行可能ファイルを簡単に設定し、各テストを個別に実行できます。テストランナーを使用して、すべてのテストプロセスを生み出します。
アップデート:
私はこれについてやや気が変わった。多くのテストで大きなプログラムを取得すると、何百ものテスト実行可能ファイルをリンクすることは非常に遅くなります。私の新しい好みは、ライブラリのすべてのテストを実行可能ファイルに入れ、テスト実行可能ファイルに渡されたコマンドライン引数を使用して呼び出すテストを選択することです。
これにより、実行可能ファイルの数は数百から数十に減少しますが、実行テストの利点は個別に保持されます。