単体テストのために、ネットワーク応答をモックアップする必要があります。通常、応答はバイト ストリームであり、const vector<uint8_t>
. ただし、単体テストでは、CPP ファイルにハードコードされているか、同じソリューションのファイルから読み取られたデータを使用してベクトルを生成したいと考えています。サンプル データは約 6 kb です。googletestを使用するときにデータを配置する場所に関する一般的なガイダンスは何ですか?
2 に答える
おそらく (a) テスト ケースがデータを読み取るだけの役割のために、大量のデータ シーケンスが必要になる場合があります。これは、アクセス可能な (クラス) グローバル データである可能性もあり
const
ます。
おそらく (b) テスト ケースが読み取り、変更、または破棄する役割のために、大量のデータ シーケンスが必要な場合。これは、テスト ケースごとに再初期化する必要があり、const
アクセスできません。
おそらく両方。どちらの場合も、従来の googletest の実装では、テスト フィクスチャを使用
してデータの取得をカプセル化し、フィクスチャの仮想メンバー関数の実装でデータを取得し、フィクスチャのSetup()
getter メソッドを介してデータにアクセスしていました。
次のプログラムは、ファイルから取得したケースごとの可変データとグローバル定数データの両方を提供するフィクスチャを示しています。
#include <vector>
#include <fstream>
#include <stdexcept>
#include "gtest/gtest.h"
class foo_test : public ::testing::Test
{
protected:
virtual void SetUp() {
std::ifstream in("path/to/case_data");
if (!in) {
throw std::runtime_error("Could not open \"path/to/case_data\" for input");
}
_case_data.assign(
std::istream_iterator<char>(in),std::istream_iterator<char>());
if (_global_data.empty()) {
std::ifstream in("path/to/global_data");
if (!in) {
throw std::runtime_error(
"Could not open \"path/to/global_data\" for input");
}
_global_data.assign(
std::istream_iterator<char>(in),std::istream_iterator<char>());
}
}
// virtual void TearDown() {}
std::vector<char> & case_data() {
return _case_data;
}
static std::vector<char> const & global_data() {
return _global_data;
}
private:
std::vector<char> _case_data;
static std::vector<char> _global_data;
};
std::vector<char> foo_test::_global_data;
TEST_F(foo_test, CaseDataWipe) {
EXPECT_GT(case_data().size(),0);
case_data().resize(0);
EXPECT_EQ(case_data().size(),0);
}
TEST_F(foo_test, CaseDataTrunc) {
EXPECT_GT(case_data().size(),0);
case_data().resize(1);
EXPECT_EQ(case_data().size(),1);
}
TEST_F(foo_test, HaveGlobalData) {
EXPECT_GT(global_data().size(),0);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
ケース (a) の場合、 をサブクラス化することによって、グローバルな SetUp
メンバー関数でデータを取得することも検討できますが、::testing::Environment
そのようにすることを好む一般的な理由はわかりません。
...またはハードコーディングしますか?
次に、テスト データをファイルに保持するか、テスト ソースにハード コードするかという問題に進みます。この時点で満足している読者は、これから退屈するだけです。
一般的な問題として、これは状況に応じた判断の問題であり、googletest の使用が実質的にスケールを傾けるとは思いません。主な考慮事項は、テストスイートを再構築せずにテストデータの項目を変更できることが望ましいかということだと思います。
この項目を変更するためにテスト スイートを再構築するコストは無視できないものであり、関連するテスト コードとは関係なく、項目の内容が将来変更されると予想しているとします。または、関連するテスト コードとは関係なく、テスト対象のシステムのさまざまな構成に合わせて変更できます。その場合は、テスト スイートの実行時パラメーターで選択できるファイルまたはその他のソースから項目を取得するのが最善です。googletest では、サブクラス化class ::testing::Environment
は、テスト スイート リソースをパラメータ化して取得するために設計された機能です。
実際には、テスト データ項目の内容が関連するテスト コードと緩やかに結合されている場合、それをテスト ケースにハードコーディングすることは賢明な選択とは言えません。(そして、テスト ファイルは、他の種類のランタイム コンフィギュレーターとは対照的に、ソース コードと同じシステムでバージョン管理できるという貴重な特性を持っています。)
テスト データ項目の内容が関連するテスト コードとしっかりと結合されている場合、データ ファイルから抽出するのではなく、ハード コードする傾向があります。ただの偏見であり、独断的にコミットしているわけではありません。おそらく、テスト スイートは、テスト管理システムや欠陥管理システムにもフックされている XML ファイルなどからパブリック API テスト データを初期化するための堅牢なライブラリ機能を採用しています。罰金!
テスト データのファイルが主要なテスト リソース (テスト スイートが生成できないリソース) である場合、その内容は有能な保守担当者が容易に理解して操作できるテキスト データであることが望ましいと私は考えます。この設定では、もちろん、たとえば C/C++ 16 進定数のリストはテキスト データ、つまりソース コードであると考えます。テスト ファイルにバイナリ データや非常に機械指向のデータが含まれている場合、テスト スイートには、判読可能な一次リソースから生成する手段が含まれている必要があります。テスト スイートが外部ソースの「典型的な」バイナリに依存することは避けられない場合もありますが、ほとんどの場合、テスト エンジニアやバグ修正担当者が 16 進エディタの前で灰色になるという悲惨な光景を伴います。
一次テスト データはメンテナーにとって判読可能でなければならないという原則を考えると、一次テスト データは「ある種のコード」であると見なすことができます。プログラマーは調査と編集に慣れています。
ソフトウェアをテストするために 4096 個の 64 ビット符号なし整数 (ビッグ マジック テーブル) の特定のシーケンスが必要であり、関連するテスト コードに密接に結びついていると想像してください。テスト スイートの一部のソース ファイルで、巨大なベクトルまたは配列初期化子リストとしてハードコードされる可能性があります。これは、CSV 形式または CSV で区切られた行で維持されているデータ ファイルから、テスト スイートによって抽出される可能性があります。
データ ファイルからの抽出とハード コーディングに反対する場合、(Andrew McDonell の回答によると) これにより、同じソース ファイル内の他のコードのリビジョンから BMT へのリビジョンのもつれを解きほぐすことができるようになります。同様に、大量のリテラル初期化を構成するソース コードは調査不能になりがちであり、そのためメンテナンスの責任が生じることが強く求められる場合があります。
しかし、これらの点は両方とも、BMT の定義宣言がソース ファイル内で独自にコーディングされている可能性があるという観察によって反論される可能性があります。テスト データの初期化をそのようにコーディングする必要があるというのは、テスト スイートのコード レビュー ポリシーである可能性があります
。確かに狂信的なポリシーですが、すべてのテストデータ初期化子をファイルから抽出する必要があると主張するポリシーよりも狂信的ではありません。メンテナーが BMT を含むファイルに含まれる BMT を調査する義務がある場合、ファイル拡張子が.cpp
であろうが、.dat
何であろうが、違いはありません。すべての問題は、「コード」の理解度です。
ハード コーディングの場合とデータ ファイルからの抽出に反対する場合、データ ファイルからの抽出は、テスト ケースに関係のない潜在的なエラーの原因を導入する必要があることを強く主張できます。 . これは、テストの開発にオーバーヘッドを課し、本物のテストの失敗とファイルからのテスト データの取得の失敗を正しく明確に区別し、後者のすべての考えられる原因を明確に診断します。
googletest および同等の機能を備えたフレームワークの場合、この点は、::testing::Test
およびのようなポリモーフィックなフィクスチャ基底クラスを宣伝することによって、ある程度は打ち消すことができます::testing::Environment
。これらは、テスト開発者がテストケースまたはテストスイートの初期化でテストリソースの取得をカプセル化するのを容易にし、テストケースの構成テストが実行される前に、成功または診断された失敗のいずれかですべて終了するようにします。RAII は、セットアップの失敗と実際の失敗の間の問題のない分割を維持できます。
それにもかかわらず、データ ファイル ルートには削減できないファイル処理オーバーヘッドがあり、フレームワークの RAII 機能では削減できない運用オーバーヘッドがあります。データ ファイルを交換する大規模なテスト システムを扱う場合、データ ファイルは、ビルド時にのみ存在し、正しくなければならないソース ファイルよりも操作上の問題が発生しやすいだけです。データ ファイルは、実行時に見つからないか、場所が間違っているか、不正な形式のものを含んでいる可能性が高く、何らかの形で権限が拒否されているか、何らかの形で間違ったリビジョンに表示される可能性が高くなります。テスト システムでの使用は、ソース ファイルの使用ほど単純ではなく、厳格に管理されているわけでもありません。あってはならないことテストデータファイルにたまたま遭遇することは、それらに依存するテストシステムの運用上の摩擦の一部であり、その数に比例します.
ソース ファイルは、リビジョンの追跡のためにテスト データの初期化を衛生的にカプセル化できるため、コンパイルの副産物として抽出を行うプリプロセッサを使用して、ソース ファイルをハードコーディングすることはファイルから抽出することと見なすことができます。その観点から、なぜそれを抽出するために、追加の責任を伴う他の機械を使用するのでしょうか? テスト管理システムや欠陥管理システムとの提案された XML インターフェースのように、良い答えがあるかもしれませんが、「これはテスト データなので、ハードコーディングしないでください」は良い答えではありません。
テスト スイートが、テスト データ項目のさまざまなインスタンス化を呼び出すテスト対象システムのさまざまな構成をサポートする必要がある場合でも、データ項目がテスト スイートのビルド構成と一致している場合は、(衛生的に) ハードコーディングすることもできます。条件付きコンパイルで適切なハードコーディングを選択します。
これまでのところ、テスト データの初期化子をファイル ベースで分離するためのリビジョン トラッキング ヒジーンの議論に異議を唱えたことはありません。イニシャライザがハードコードされている通常のソース ファイルは、この分離を実現できるということを強調しました。そして、私はその議論を打ち負かすつもりはありませんが、テストデータ初期化子は原則として常に専用ファイルから抽出されるべきであるという狂信的な結論の手前でそれを止めたいと思います-ソースファイルかデータファイルか.
この結論に反対する理由をあれこれ考える必要はありません。その方法は、平均的なピザを食べるプログラマーが書くよりも局所的に理解しにくいテスト コードと、必要以上に、または健全であるよりもはるかに急速に気が遠くなるほど大きくなるテスト スイート ファイルの組織です。通常、テスト スイートの主要なリソースはすべて「何らかのコード」です。プログラマーのスキルセットには、コードを適切な粒度でファイルに分割して、適切なリビジョン追跡の衛生状態を確保するスキルが含まれます。これは機械的な手順ではなく、コード レビューがカバーする専門知識です。コードレビューは、テストデータの初期化を確実にすることができますし、そうすべきです。
結論: これらのさまざまな模擬ネットワーク応答に対してテスト スイートの同じビルドを実行できるようにしたい場合は、ファイルから読み取ります。一方、テスト スイートのビルド構成で不変または共変である場合は、ハード コードしないでください。