単体テストが相互に依存することは悪い習慣ですか?


9

私がこのようなユニットテストを持っているとしましょう:

let myApi = new Api();

describe('api', () => {

  describe('set()', () => {
    it('should return true when setting a value', () => {
      assert.equal(myApi.set('foo', 'bar'), true);
    });
  });

  describe('get()', () => {
    it('should return the value when getting the value', () => {
      assert.equal(myApi.get('foo'), 'bar');
    });
  });

});

だから今私は2つの単体テストを持っています。APIで値を設定します。もう1つのテストでは、適切な値が返されることを確認します。ただし、2番目のテストは最初のテストに依存しています。2番目のテストが他に依存していないことを確認するという唯一の目的.set()で、2番目のテストの前にメソッドを追加する必要get()がありますか?

また、この例ではmyApi、テストの前に1回実行するのではなく、テストごとにインスタンス化する必要がありますか?

回答:


15

はい、それは悪い習慣です。単体テストは、他の関数を独立して実行する必要があるのと同じ理由で、互いに独立して実行する必要があります。独立したユニットとして扱うことができます。

2番目のテストが他に依存していないことを確認することのみを目的として、2番目のテストのget()の前に.set()メソッドを追加する必要がありますか?

はい。ただし、これらが単なるゲッターおよびセッターメソッドである場合、それらには動作が含まれていないため、ゲッター/セッターのような方法でファットフィンガーを行うという評判がない限り、実際にテストする必要はありません。コンパイルしますが、間違ったフィールドを設定または取得します。


私の例でmyApiは、インスタンス化されたオブジェクトであるとしましょう。myApi各単体テストでインスタンス化する必要がありますか?または、テスト間でそれを再利用すると、テストで偽陽性などが発生する可能性があります。そうです、私の例は、簡略化されたゲッター/セッターですが、実際には明らかにはるかに複雑になります。
Jake Wilson、

1
@JakeWilsonなど、互いに干渉テストを回避する方法として、ユニットテストの基礎の上に行く実用ユニットテストという本、あり
rwong

別の例:JUnitを使用する場合、関数の順序は、クラスに記述した順序によって定義されません。@JakeWilson APIがステートレスの場合、再インスタンス化しなくても再利用できます。
Walfrat 2016年

2

各テストについて、arrange-act-assert構造に従うようにしてください。

  1. オブジェクトなどを配置し、既知の状態(テストフィクスチャ)にします。場合によっては、このフェーズには、自分が実際に自分がいると思っている状態にあることを示すアサートが含まれます。
  2. 行為、つまり、テストしている動作を実行します。
  3. 期待どおりの結果が得られたことを表明します。

テストでは、最初に既知の状態を作成する必要がないため、単独では意味がありません。

また、単体テストは必ずしも単一のメソッドのみをテストするわけではありません。単体テストはユニットをテストする必要があります。通常、このユニットはクラスです。のようないくつかの方法はget()、他のものとの組み合わせでのみ意味があります。

特に動的言語では、ゲッターとセッターのテストは賢明です(実際に存在することを確認するためだけです)。ゲッターをテストするには、最初に既知の値を指定する必要があります。これは、コンストラクターまたはセッターを通じて発生する可能性があります。または別の見方をすると:ゲッターのテストは、セッターとコンストラクターのテストでは暗黙的です。そしてセッター、それは常にを返しtrueますか、それとも値が変更されたときだけですか?これにより、次のテスト(疑似コード)が発生する可能性があります。

describe Api:

  it has a default value:
    // arrange
    api = new Api()
    // act & assert
    assert api.get() === expected default value

  it can take custom values:
    // arrange & act
    api = new Api(42)
    // act & assert
    assert api.get() === 42

  describe set:

    it can set new values:
      // arrange
      api = new Api(7)
      // act
      ok = api.set(13)
      // assert
      assert ok === true:
      assert api.get() === 13

    it returns false when value is unchanged:
      // arrange
      api = new Api(57)
      // act
      ok = api.set(57)
      // assert
      assert ok === false
      assert api.get() === 57

以前のテストの状態を再利用すると、テストが非常に脆弱になります。テストを並べ替えたり、テストの正確な値を変更したりすると、明らかに関連のないテストが失敗する可能性があります。特定の状態を想定すると、実際に失敗するはずのテストに合格した場合に、バグを隠すこともできます。これを防ぐために、一部のテストランナーには、テストケースをランダムな順序で実行するオプションがあります。

ただし、前のテストで提供された状態を再利用する場合があります。特に、テストフィクスチャの作成に時間がかかる場合、多くのテストケースを1つのテストスイートにまとめますか。これらのテストは脆弱ですが、より迅速かつ頻繁に実行できるため、今でも価値があるかもしれません。実際には、テストに手動コンポーネントが含まれる場合、大規模なデータベースが必要な場合、または状態マシンをテストする場合は、テストを組み合わせることが望ましいです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.