web-dev-qa-db-ja.com

単体テストのコマンドライン引数を提供することは悪い習慣ですか

私はC++を使用しており、gtestをメインフレームワークとして使用しています。画像を入力として受け取り、エッジ検出された画像を返す、エッジ検出機能があるとしましょう。チェックできる画像が3つあります。

次のように自己完結型で書く方が良いですか

void EdgeCheck(const std::string& input, const std::string& ans) {
    cv::Mat in_img = cv::imread(input);
    cv::Mat ans_img = cv::imread(ans);
    // Do some checks here
}

TEST(Edge) {
    EdgeCheck(std::string("path to image 1"),std::string("path to ans image 1"));
    EdgeCheck(std::string("path to image 2"),std::string("path to ans image 2"));
    EdgeCheck(std::string("path to image 3"),std::string("path to ans image 3"));
}

int main(int argc, char** argv) {
    return UnitTest::RunAllTests();
}

したがって、テストは単純な./unit-testまたは次のように書く方が良いですか

TEST(Edge) {
    cv::Mat in_img = cv::imread(argv[1]);
    cv::Mat ans_img = cv::imread(argv[2]);

    // Do some check here
}

int main(int argc, char** argv) {
    return UnitTest::RunAllTests();
}

しかしそれはによって実行されなければなりません

./unit-test image1 ans_image1
./unit-test image2 ans_image2
./unit-test image3 ans_image3

私の理解から、 nitTest ++コマンドライン引数 による最初のアプローチの利点は、「適切に記述された単体テストは自己完結型であり、テストをパラメーター化すると、テストは単なるテストではなくなります。これは単体テストではなく関数になりました。」

ただし、2番目のアプローチの利点は、image4とimage5を追加する場合にコードを再コンパイルする必要がなく、bashスクリプトで実行できるという点で、柔軟性が高いことです。自己完結型ではありません。

どのアプローチがより良い実践と考えられていますか?

3
user3667089

これは、テストを管理して環境全体に統合する方法についてのすべてです。私はあなたがする必要があると思います

  • 新しいテストを簡単に追加
  • すべてのテストを一度に、おそらくテストスイートの一部として実行する
  • 1つの失敗したテストが他の独立したテストの実行を妨げないことを確認してください
  • その後、失敗したテストと失敗しなかったテストのログを取得します。
  • バグ修正を適用した後、テストのサブセットまたは単一のテストを実行して、失敗しないことを確認します
  • 特別な知識がなくても、誰でも簡単にテストを実行できる

2つのソリューションの両方を見ると、コマンドラインでパラメーター化することで、それらを個別に実行する場合と「一度にすべて」を実行する場合の切り替えの柔軟性が向上し、失敗したテストが他のテストに影響を与えないことを確認しやすくなります。テスト。必要に応じて、それらをより大きなテストスイートに統合する方が簡単かもしれません。ただし、この柔軟性は無料ではありません。パラメータを知る必要があるので、コマンドラインプログラムは、間違った方法で呼び出された場合、どの種類のパラメータが期待されるかを説明するメッセージを提供し、テスト実行スクリプトはテストの不可欠な部分である必要があります。

したがって、スクリプトと一緒にコマンドラインプログラムを自己完結型コンポーネントとして扱い、統合されたドキュメントを提供する限り、パラメータ化されたアプローチに問題はありません。何らかの不思議な理由でそれが「自己完結型」ではない場合は、1つのプログラムに統合しますが、私が述べた他の要件を解決するにはさらに努力が必要かもしれないという事実を受け入れてください。

2
Doc Brown

自動テストは、渡されたパラメーターに依存せずにパラメーター化できます。多くの場合、ハイブリッドアプローチが最も役立ちます。

たとえば、コマンドライン引数なしで実行すると、テストは構成されたディレクトリをスキャンして、そのディレクトリ内のすべてのイメージを反復処理する場合があります。

この場合、ディレクトリを上書きできると便利な場合があります。そのための一般的な方法は、コマンドラインパラメータを指定するか、環境変数を指定することです。または、ユーザーはデフォルトのディレクトリに追加の画像を配置して、テストを拡張することもできます。

適切に作成された単体テストは自己完結型であり、テストをパラメーター化すると、テストは単なるテストではなくなります。単体テストではなく関数になりました

@Dymengはあなたがテストしているのはおそらく実際にはユニットテストではなく統合テストであることは正しいですが、それは本当に重要ではありません。単体テストはパラメーター化できるため、統合テストも可能です。

自己完結型であるという点は重要です。コマンドライン引数を指定せずにプロジェクトでテストを実行できるはずです。テストを実行したい人がいる場合、テストを実行するための正しいパラメータを知るには特別な知識が必要になるためです。しかし、その事実は、他のデータセットでテストを実行する方法を公開することを妨げるべきではありません。

1
Samuel

申し訳ありませんが、これは今日聞いた最悪の考えです。いくつかの理由があります。

  • ユニットテストを実行するユーザーは、正しい位置で正しいパラメーターを渡す必要があります。そうしないと、ユニットテストが失敗します。
  • gtestは独自のオプションのパラメーターを使用しているため、テストパラメーターの場所(argv [1]または他のインデックス)がわからない

実際にデータを(変数に)テストし、(可能であれば)ファイルからロードしない場合は、単体テストに最適です。

1
BЈовић

Roy Osheroveによる The Art of Unit Testing によると、彼はユニットテストの次の特性を述べています。

1.2。優れた単体テストの特性

単体テストには次のプロパティが必要です。

  1. それは自動化され、再現可能でなければなりません。
  2. 実装は簡単です。
  3. いったん作成したら、将来の使用のために残しておく必要があります。
  4. 誰でも実行できるはずです。
  5. ボタンを押すだけで実行されます。
  6. すぐに実行されるはずです。

コマンドライン引数を使用することで、テストを繰り返したり自動化したりすることができなくなります。あなたのコンピューターに行き、「テストの実行」を押して実行してもらえますか?答えは単にノーです。

これらの画像を使用してテストする別の方法を見つけることをお勧めします。多分それらはあなたのプロジェクトの一部であり、相対パスをハードコードしていますか?

1
Nicolas