私が書いたものではない小さな学習プログラムのテストを(Googleテストフレームワークを使用して)書く必要があります。(コマンド ラインからモードを取得するか、実行時に取得するだけの小さなコンソール ゲームです) 問題があります: ソース コードを変更することはできませんが、ほとんどすべてのメソッドで cout と cin が使用されています。そして私の質問は、「テスト中にプログラムのリクエスト(cin)に答える方法(文字列からcinのデータを取得するようなもの)ですか?」.
4 に答える
main()
制御できる(またはテストする関数の前に呼び出される他の関数)と仮定すると、std::cin
読み取り元とstd::cout
書き込み先を変更できます。
int main(int ac, char* av[]) {
std::streambuf* orig = std::cin.rdbuf();
std::istringstream input("whatever");
std::cin.rdbuf(input.rdbuf());
// tests go here
std::cin.rdbuf(orig);
}
(同様にstd::cout
)
この例では、 の元のストリーム バッファを保存して、 をstd::cin
終了する前に置き換えることができるようにしmain()
ます。std::cin
次に、文字列ストリームから読み取るように設定します。他のストリーム バッファでもかまいません。
cin
and をcout
直接使用しないことで、クラスのテスト容易性を向上させることができます。代わりにistream&
andostream&
を使用して、入力ソースと出力シンクをパラメーターとして渡します。これは、依存性注入のケースです。その場合、 のstd::stringstream
代わりに a を渡すcin
ことができるため、指定された入力を提供して、テスト フレームワークからの出力を取得できます。
とはいえ、cin と cout をstringstream
s に (少なくとも一時的に) 変更することで、同様の効果を得ることができます。これを行うには、 std::stringbuf を設定 (または から「借用」std::stringstream
) し、 を使用して使用されるcin.rdbuf(my_stringbuf_ptr)
を変更します。テスト ティアダウンでこの変更を元に戻すことができます。これを行うには、次のようなコードを使用できます。streambuf
cin
stringbuf test_input("One line of input with no newline", ios_base::in);
stringbuf test_output(ios_base::out);
streambuf * const cin_buf = cin.rdbuf(&test_input);
streambuf * const cout_buf = cout.rdbuf(&test_output);
test_func(); // uses cin and cout
cout.rdbuf(cout_buf);
cin.rdbuf(cin_buf);
string test_output_text = test_output.str();
私の理解では、次のことを実行する必要があります。
- ターゲットの実行可能ファイル (ゲーム) を起動/開始します。
- テスト データをターゲット実行可能ファイルに送信します。
- ターゲット実行可能ファイルから出力を取得します。
- 出力を期待される結果と比較します。
標準 C++ 言語には、他のプログラムと通信するための標準機能がありません。オペレーティング システム (指定しなかったもの) からのヘルプが必要になります。
C++ のみを使用するか、OS 固有の呼び出しを使用せずに、次のことをお勧めします。
- テスト入力をファイルに書き込みます。
- 実行可能ファイルを実行し、テスト入力ファイルを入力としてパイプし、出力を結果ファイルにパイプします。
- 結果ファイルを読み取って分析します。
それ以外の場合は、OS API を検索して、I/O リダイレクション ドライバーへの書き込み方法を見つけてください。
コードを変更できないとおっしゃいましたが、できるかのようにお答えします。現実の世界では、通常、テストに対応するために (小さな) 変更が許可されています。
1 つの方法は、外部入力 (DB、ユーザー入力、ソケットなど) を必要とする呼び出しを仮想の関数呼び出しでラップして、それらをモックアウトできるようにすることです。(下の例)。しかし、最初に、テストに関する本をお勧めします。 「レガシー コードを効果的に使用する」は、レガシー コードだけに限定されない手法をテストするための優れた本です。
class Foo {
public:
bool DoesSomething()
{
string usersInput;
cin >> usersInput;
if (usersInput == "foo") { return true; }
else { return false; }
}
};
次のようになります。
class Foo
{
public:
bool DoesSomething() {
string usersInput = getUserInput();
if (usersInput == "foo") { return true; }
else { return false; }
}
protected:
virtual std::string getUserInput() {
string usersInput;
cin >> usersInput;
return usersInput;
}
};
class MockFoo : public Foo {
public:
void setUserInput(std::string input) { m_input = input }
std::string getUserInput() {
return m_input;
}
};
TEST(TestUsersInput)
{
MockFoo foo;
foo.setUserInput("SomeInput");
CHECK_EQUAL(false, foo.DoesSomething());
foo.setUserInput("foo");
CHECK_EQUAL(true, foo.DoesSomething());
}