web-dev-qa-db-ja.com

単体テストが他方を「信頼する」ことは悪い習慣ですか?

次の2つの関数があるとします。

_function storeObject(object) {
    // Connect to database
    // Prepare query
    // Execute query
}

function retrieveObjectWith(id) {
    // Connect to database
    // Prepare query
    // Execute query
    // Parse results

    return object;
}
_

そして、私は彼らにテストを書きたいと思っています:

_function testStore() {
    storeObject(storedObject)

    // Connect to mocked database
    // Prepare query to retrieve stored object
    // Execute query and retrieve stored object

    [retrievedObject] should equal [storedObject]
}

function testRetrieve() {
    // Connect to mocked database
    // Prepare query to store object
    // Execute query and store object

    retrievedObject(storedObject)

    [retrievedObject] should equal [storedObject]
}
_

次のように、最初のテストの結果を「信頼」した場合、2番目のテストを簡略化できます。

_function testRetrieve() {
    // Since I have a separate test for storeObject, I can use it here
    storeObject(testObject)
    retrievedObject = retrieveObjectWithId(testObject.id)

    [retrievedObject] should equal [testObject]
}
_

質問は

テスト内でロジックを書き直す代わりにstoreObject(object)を使用することの短所はありますか?

これを行うのは悪い習慣と考えられていますか?

5
appa yip yip

テスト中のシステムは何ですか?多くの場合、メソッドを分離してテストすることは賢明ではありません。多くの場合、システムの価値は複数の方法の相互作用を通じて提供されます。次に、値がシステムによって提供されていることを確認するテストを作成するときに、複数のメソッドを呼び出しても問題ありません。

テストの例は、システムが提供する可能性のある3種類の価値を示唆しています。まず、データベースの使用方法に関して2つの動作があります。

  • シナリオ:データベースへの書き込みは特定の形式を使用します
    データベースを与える
    私がstoreObject({ id: 123, name: "foo", ... })
    データベーステーブルobjectsに行_(123, "foo", ...)_が含まれています

  • シナリオ:データベースからの読み取りは特定の形式を理解します
    データベースにオブジェクト_(789, "bar", ...)_を指定します
    私がretrieveObject(789)
    オブジェクトは_{ id: 789, name: "bar", ...}_です

これらのテストは、システムが特定のデータベーススキーマとどのように相互作用するかを検証するため、統合テストである可能性があります。他のシステムが同じデータベースを使用する場合、またはシステムが複数のデータベーススキーマをサポートする必要がある場合、このようなテストは多くの価値があります。

ただし、単体テストの場合は、完全に異なるプロパティに興味がある可能性があります。

  • シナリオ:保存されたオブジェクトを取得できる
    データベースを与える
    そしていくつかのobjectid
    私がstoreObject(object)
    そして私retrieveObject(id)
    その後、同じobjectを受け取ります

ストア/リトリーブは内部でどのように機能しますか? 2つの機能に互換性があれば問題ありません。保存している特定のオブジェクトは何ですか?同じオブジェクトが返ってくる限り、問題ありません(→テストのパラメーター化を検討してください)。

この3番目のテストは、システムが提供することになっている値を説明している可能性があるため、必ず存在する必要があります。使用される特定のデータ形式のような実装の詳細をテストすると、システムのデバッグが容易になりますが、テストがより脆弱になります。システムを変更して別のデータ形式を使用する場合、システムが3番目のテストで主な値を提供し続けても、最初の2つのテストを更新する必要がある場合があります。

「すべてのテストは1つのものだけをテストする」というマントラは、経験則ですが、このコンテキストで「1つのもの」が何であるかを考える場合に限られます。特に、テスト対象のシステムが何であるか(関数?クラス?モジュール?サービス?)と、このシステムが提供する価値について検討してください。次に、システムがこの値を提供するhowに焦点を当てずに、システムがこの値を提供することを確認するテストを記述します。

9
amon

うーん、ダメ。ユニットテストはone事をテストすることになっています。メソッドを実行する特定の方法の1つにすることも、それが十分に簡単であればメソッド全体にすることもできますが、複数のメソッドをテストするためのものではありません。したがって、differentメソッドの有効性を想定した1つのテストは、標準の操作手順です。他のメソッドにバグがあった場合、それはother単体テストに現れます。 (これが、コードカバレッジがテストで重要な理由です。)

4
Kilian Foth