web-dev-qa-db-ja.com

単体テストの実行順序を強制することは悪い習慣ですか?

複数のサブモジュールで構成されるプロジェクトのテストを書いています。私が作成した各テストケースは互いに独立して実行され、テスト間のすべてのデータをクリアします。

テストは独立して実行されますが、場合によっては複数のサブモジュールが必要になるため、実行順序を適用することを検討しています。たとえば、サブモジュールがデータを生成し、別のモジュールがデータに対してクエリを実行しています。データを生成するサブモジュールにエラーが含まれている場合、サブモジュール自体が正常に機能していても、クエリサブモジュールのテストも失敗します。

私がテストしている主な機能は、最初のサブモジュールからのみデータを取得するブラックボックスリモートサーバーへの接続であるため、ダミーデータを処理できません。

この場合、テストの実行順序を強制しても大丈夫ですか、それとも悪い習慣ですか?この設定には匂いがするような気がしますが、これ以上の方法はありません。

編集:問題は 1つのテストが別のテストのセットアップであるテストをどのように構成するか? です。「前の」テストはセットアップではなく、セットアップを実行するコードをテストするためです。

84
Ali Rasim Kocal

私がテストしている主な機能は、最初のサブモジュールからのみデータを取得するブラックボックスリモートサーバーへの接続であるため、ダミーデータを処理できません。

これは私にとって重要な部分です。 「単体テスト」と「互いに独立して実行される」について話すことができますが、これらはすべて、このリモートサーバーに依存し、「最初のサブモジュール」に依存しているように聞こえます。したがって、すべてが密結合で外部状態に依存しているように聞こえます。そのため、実際には統合テストを作成しています。これらのテストを特定の順序で実行することは、外部要因に大きく依存するため、非常に正常です。順序付けされたテスト実行、および問題が発生した場合にテスト実行を早期に終了するオプションは、統合テストに完全に受け入れられます。

ただし、アプリの構造を再検討する価値もあります。最初のサブモジュールと外部サーバーをモックアウトできると、他のすべてのサブモジュールの真の単体テストを作成できる可能性があります。

236
David Arno

はい、それは悪い習慣です。

一般に、ユニットテストは、単一のコードユニット(既知の状態に基づく単一の関数など)をテストすることを目的としています。

実際に発生する可能性のある一連のイベントをテストする場合は、統合テストなどの別のテストスタイルが必要です。これは、サードパーティのサービスに依存している場合はさらに当てはまります。

このようなものを単体テストするには、ダミーデータを挿入する方法を理解する必要があります。たとえば、Webリクエストをミラーリングするが、ローカルのダミーデータファイルから既知のデータを返すデータサービスインターフェイスを実装します。

33
Paul

提案する強制実行順序は、最初の失敗後にテスト実行も中止した場合にのみ意味があります。

最初の失敗時にテスト実行を中止するということは、各テスト実行で単一の問題しか発見できず、先行するすべての問題が修正されるまで新しい問題を見つけることができないことを意味します。最初に実行するテストで修正に1か月かかる問題が見つかった場合、その月の間、テストは実質的に実行されません。

最初の失敗時にテストの実行を中止しない場合、失敗した各テストはとにかく調査する必要があるため、強制された実行順序は何も購入しません。データ生成サブモジュールでも特定された障害が原因で、クエリサブモジュールのテストが失敗していることを確認する場合でも、.

私が与えることができる最高のアドバイスは、依存関係の失敗がテストの失敗の原因となっているときを簡単に識別できるようにテストを書くことです。

あなたが言及している匂いは、間違った制約とルールのセットをテストに適用したものです。

単体テストは、「自動テスト」または「プログラマーによる自動テスト」と混同されることがよくあります。

単体テストは小さく、独立しており、高速でなければなりません。

一部の人々はこれを "プログラマーによって書かれた自動テストは小さく独立で高速でなければならない"と誤って読みます。しかし、それは単に、テストが小さく、独立していて高速ではない場合、それらは単体テストではないことを意味します。したがって、単体テストの一部のルールは、できない、できない、またはできない必要がないテストに申し込む。簡単な例:ビルドごとにユニットテストを実行する必要があります。これは、高速でない自動テストでは実行しないでください。

テストが単体テストではないということは、1つのルールをスキップしてテスト間の相互依存を許可できることを意味しますが、見逃していて再導入する必要のある他のルールがあることもわかりました-別の質問の範囲のための何か。

7
Peter

上記のように、実行しているのは統合テストのようですが、次のように述べています。

たとえば、サブモジュールがデータを生成し、別のモジュールがデータに対してクエリを実行しています。データを生成するサブモジュールにエラーが含まれている場合、サブモジュール自体が正常に機能していても、クエリサブモジュールのテストも失敗します。

そして、これはリファクタリングを始めるのに良い場所かもしれません。データに対してクエリを実行するモジュールは、最初の(データ生成)モジュールの具体的な実装に依存するべきではありません。代わりに、そのデータにアクセスするためのメソッドを含むインターフェースを挿入する方が良いでしょう。そして、これをモックアウトしてクエリをテストできます。

例えば.

あなたが持っている場合:

class Queries {

    int GetTheNumber() {
        var dataModule = new Submodule1();
        var data = dataModule.GetData();
        return ... run some query on data
    }
}

代わりに:

interface DataModule {
    Data GetData();
}


class Queries {

    IDataModule _dataModule;

    ctor(IDataModule dataModule) {
       _dataModule = dataModule;
    }

    int GetTheNumber() {
        var data = _dataModule.GetData();
        return ... run some query on data
    }
}

これにより、データソースのクエリから依存関係が削除され、特定のシナリオに対して簡単に繰り返し可能な単体テストをセットアップできます。

6
Paddy

他の回答では、テストの順序付けは悪いことです(ほとんどの場合そうです)が、テストの実行に順序を強制することには1つの正当な理由があります。遅いテスト(つまり、統合テスト)が速いテスト(テスト)の後に実行されるようにすることです。他の外部リソースに依存しない)。これにより、より多くのテストをより速く実行できるようになり、フィードバックループを高速化できます。

6
Mike Holler