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


84

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

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

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

この場合、テストの実行順序を強制してもいいですか、それとも悪い習慣ですか?このセットアップには臭いがあるように感じますが、より良い方法は見つかりません。

編集:質問は、1つのテストが別のテストのセットアップである場合のテストの構成方法からですか?「前の」テストはセットアップではなく、セットアップを実行するコードをテストするためです。



123
リモートサーバーへの接続をテストしている場合、それらは定義上単体テストではありません。
テラスティン

9
最初の答えはここで私を混乱させました。なぜなら、あなたのタイトルで「悪い習慣ですか?」そして、あなたの質問の要約に「大丈夫ですか?」と書いた。「はい」または「いいえ」のいずれかで答えると、それらの1つが混乱します。
リース

8
統合テストのセットを作成しているように聞こえます。その場合でも、1つのテストが他のテストに依存するべきではありません。
低空飛行ペリカン

17
順序が重要な場合は、おそらく間違っています。
マークロジャース

回答:


236

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

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

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


14
言うまでもなく、一部のテストでは、リモートサーバーが利用できないときに予期される動作が発生することを具体的に確認する必要があります。
アレクサンダー

2
または、おそらく、実際には統合テストを作成するつもりであるため、データをモックアウトしても、これらのテストで達成しようとしていることを達成することはできません。
ガイシャルナット

10
問題は、Junitの名前に「ユニット」が含まれている可能性が高いことです。
するThorbjörnRavnアンデルセン

7
@ThorbjørnRavnAndersenまさに。統合テストは、「実際の」単体テストよりもはるかに有用であり、作成するのがはるかに難しいため、ユニットテストではなく統合テストを自然に記述します。しかし、人気のあるテストフレームワークは単体テストの概念にちなんで命名されたため、この用語は採用され、現代用語では「自動テスト」を意味するようになりました。
メイソンウィーラー

1
@MasonWheelerまたは受入テストを意味するために非技術マネージャーによって共同で選ばれた
TKK

32

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

一般に、単体テストは、単一のコードユニット(たとえば、既知の状態に基づく単一の機能)をテストすることを目的としています。

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

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


8
同意した。この混乱は、多くの人が統合テストはエンドツーエンドでなければならないという考えを持ち、「ユニットテスト」を使用して1つのレイヤーのみをテストするテストを指すという事実に起因すると思います。
オート

8
@autophage:間違いなくこれに同意します。実際、私は定期的に自分自身が同じ罠に陥る見つけることはあまりそれに同意することが罠であることを合意にもかかわらず 😂
軌道上での明度レースは

16

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

最初の失敗でテスト実行を中止すると、各テスト実行で発見できる問題は1つだけになり、前の問題がすべて修正されるまで新しい問題を見つけることができなくなります。最初に実行するテストで修正に1か月かかる問題が見つかった場合、その月の間は事実上テストは実行されません。

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

私ができる最善のアドバイスは、依存関係の失敗がテストの失敗を引き起こしていることを簡単に識別できるような方法でテストを書くことです。


7

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

ユニットテストは、しばしば「自動テスト」または「プログラマーによる自動テスト」と混同されます。

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

一部の人々はこれを「プログラマーによって書かれた自動化されたテストは小さく独立した高速でなければならない」と誤って読んでいます。ただし、テストが小さく、独立しておらず、高速でない場合、ユニットテストではないため、ユニットテストのルールの一部は、テストに適用しない、適用できない、または適用しない必要があります。簡単な例:ビルドのたびにユニットテストを実行する必要があります。これは、高速ではない自動テストでは実行しないでください。

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


6

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

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

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

例えば

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

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

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


2
私は、注文を執行するのではなく、特定の単体テストがゆっくりと実行されている理由を調査する傾向があります。単体テストは高速になるはずです。
ロビーディー

OPは、これらのテストの一部ではダミーデータを操作できないと述べています。これは、何らかのデータベースヒットが発生し、すべてのテストが遅くなることを意味します(自然に高速に実行されるはずの一部の真のユニットテストですら)。データベースヒットを必要としない他のテストがある場合、ディスクまたはネットワークヒットを必要とするものよりもはるかに高速に実行されます。
マイクホラー

2
あなたは両方とも正しいと思います。Robbieは、単体テストは小さくて高速で、依存関係から分離する必要があるため、順序は重要ではありません。ランダムな順序付けは、その独立性を強化することにより、より優れた設計を促進します。マイクは、より高速なテストを最初に実行することが統合テストに非常に優れていることは正しいです。上記の回答のように、問題の一部は、単体テストと統合テストの用語です。
WillC

@MikeHollerそれから、それらは単体テストではありません。ユニットテストが何であるかに関して本当に混乱がないはずです。
ロビーディー

@RobbieDee OPが使用していた用語を使用していました。これらは本当の単体テストではないことを理解しています。用語について戦いたい場合は、OPを使用して育ててください。(したがって、以前のコメントで「真の単体テスト」で明確にした理由)
マイク・ホラー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.