web-dev-qa-db-ja.com

単体テストアンチパターンカタログ

アンチパターン:実際のアンチパターンと単純な悪いパターンを正式に区別するには、少なくとも2つのキー要素が必要です習慣、悪い習慣、または悪い考え:

  • 最初は有益であるように見えるが、最終的には有益な結果よりも悪い結果をもたらす、アクション、プロセス、または構造の繰り返しパターン
  • 明確に文書化され、実際の実践で実証され、再現可能なリファクタリングされたソリューション。

あなたが一度に「野生」で見たTDDアンチパターンに投票してください。
James Carrのブログ投稿 および testdrivendevelopment yahoogroupに関する関連議論

「名前のない」ものを見つけた場合は、「em」も投稿してください。 アンチパターンごとに1つの投稿をお願いします

私の関心は、上位n個のサブセットを見つけて、近い将来のお弁当会で議論できるようにすることです。

203
Gishu

Second Class Citizens-テストコードは本番コードほどリファクタリングされておらず、多くの重複コードが含まれているため、テストの維持が困難です。

70
Ilja Preuß

フリーライド/ピギーバック-James Carr、Tim Ottinger
テストするための新しいテストケースメソッドを記述するのではなく、another/distinct feature/functionalityの場合、新しいアサーション(および対応するアクション、つまりAAAからのActステップ)が既存のテストケースに沿って進みます。

67
Gishu

ハッピーパス

テストは、境界と例外をテストせずに、ハッピーパス(つまり、期待される結果)にとどまります。

JUnitアンチパターン

64
Geoglyph

地元のヒーロー

実行するために記述された開発環境に固有の何かに依存するテストケース。その結果、テストは開発ボックスで成功しますが、誰かが他の場所で実行しようとすると失敗します。

隠された依存関係

ローカルヒーローに密接に関連しており、テストを実行する前に既存のデータを入力する必要がある単体テストが必要な単体テスト。そのデータが入力されていなかった場合、テストは失敗し、開発者にそれが何を望んだのか、またはその理由はほとんど示されません。


悲しいことに、特定のプロダクションシステムで常に同期していない曖昧で多様な.iniファイルに依存する古代の.dllで、これまで何度も見ました。ため息。

59
annakata

チェーンギャング

特定の順序で実行する必要がある2つのテスト、つまり、1つのテストはシステムのグローバル状態(グローバル変数、データベース内のデータ)を変更し、次のテストはそれに依存します。

これは、データベーステストでよく見られます。テストはteardown()でロールバックする代わりに、データベースへの変更をコミットします。別の一般的な原因は、グローバル状態への変更が、テストが失敗した場合にクリーンアップするtry/finallyブロックにラップされていないことです。

58
Aaron Digulla

The Mockery
時々、モックがうまくできて便利です。しかし、開発者は自分自身を失い、テストされていないものをモックアウトしようとすることがあります。この場合、単体テストには非常に多くのモック、スタブ、および/または偽物が含まれているため、テスト対象のシステムはまったくテストされていません。代わりに、モックから返されるデータがテスト対象です。

出典:James Carrの投稿。

56
Gishu

サイレントキャッチャー-ケリー?
例外がスローされた場合に合格するテスト。実際に発生する例外が、開発者が意図したものと異なるものであっても。
参照: Secret Catcher

[Test]
[ExpectedException(typeof(Exception))]
public void ItShouldThrowDivideByZeroException()
{
   // some code that throws another exception yet passes the test
}
40
Gishu

インスペクター
100%のコードカバレッジを達成するためにカプセル化に違反するが、オブジェクトで何が行われているのかを十分に知っているため、リファクタリングを行うと既存のテストが中断され、変更を反映する必要がある単体テスト単体テストで。


'メンバー変数を公開せずにメンバー変数をテストするにはどうすればよいですか?just単体テストですか?

34
Gishu

過剰なセットアップ-James Carr
テストを開始するためにも巨大なセットアップが必要なテスト。場合によっては、数百行のコードを使用して1つのテストの環境を準備し、いくつかのオブジェクトを使用します。これにより、すべてのセットアップの「ノイズ」が原因でテスト対象を確認することが難しくなります。 (ソース: ジェームズ・カーの投稿

34
Gishu

アナルプローブ

Javaの setAccessible(true) を使用してプライベートフィールドを読み取るか、保護されたフィールド/メソッドにアクセスするためにクラスを拡張するか、特定のパッケージでテストして、パッケージのグローバルフィールド/メソッドにアクセスします。

このパターンが表示される場合、テスト中のクラスはデータ隠蔽を多く使用しています。

これとInspectorの違いは、テスト対象のクラスが、テストする必要があるものさえ隠そうとすることです。したがって、目標は100%のテストカバレッジを達成することではなく、何でもテストできるようにすることです。プライベートフィールドのみ、引数のないゲッターのないrun()メソッドのみを含むクラスを考えてください。ルールを破らずにこれをテストする方法はありません。


Michael Borgwardtのコメント:これは実際にはテストのアンチパターンではなく、テスト対象のコードの欠陥に対処するのが実用的です。もちろん、これらの欠陥を修正する方が良いですが、サードパーティのライブラリの場合は不可能かもしれません。

アーロン・ディグラ:賛成です。たぶん、このエントリはアンチパターンではなく、「JUnit HOWTO」wikiにより適しています。コメント?

32
Aaron Digulla

名前のないテスト-ニック・ペロー

バグトラッカーで特定のバグを再現するために追加され、作成者が独自の名前を保証しないと考えるテストが追加されます。既存の不足しているテストを強化する代わりに、testForBUG123という新しいテストが作成されます。

2年後、そのテストが失敗した場合、最初にバグトラッカーでBUG-123を見つけてテストの目的を把握する必要があります。

26
npellow

スローポーク

実行速度が非常に遅い単体テスト。開発者がそれを開始するとき、彼らはバスルームに行き、煙をつかむか、さらに悪いことに、一日の終わりに帰宅する前にテストを開始する時間があります。 (ソース: ジェームズ・カーの投稿

a.k.a.必要な頻度で実行されないテスト

25
Gishu

バタフライ

現在の日付を含む構造のように、常に変化するデータを含む何かをテストする必要があり、結果を固定値に絞り込む方法はありません。 ugい部分は、この値をまったく気にしないということです。値を追加せずにテストをより複雑にするだけです。

その翼のコウモリは、世界の反対側にハリケーンを引き起こす可能性があります。-エドワード・ローレンツ、 バタフライ効果

20
Aaron Digulla

フリッカーテスト(出典:Romilly Cocking)

特定の時間ではなく、たまに時々失敗するテストで、通常はテスト内の競合状態が原因です。通常、JMSなどの非同期のものをテストするときに発生します。

おそらく、「 Wait and See 」アンチパターンおよび「 The Sleeper 」アンチパターンに設定されたスーパー。

ビルドは失敗しました、まあ、もう一度ビルドを実行してください。-匿名開発者

19
Stuart

待って見る

何らかのセットアップコードを実行し、テスト対象のコードが期待どおりに機能したかどうかを「確認」する前に、特定の時間を「待機」する必要があるテスト。 Thread.sleep()または同等の機能を使用するtestMethodは、「Wait and See」テストである可能性が最も高いです。

通常、このテストは、電子メール、http要求などのシステム外部のイベントを生成するコードをテストする場合、またはファイルをディスクに書き込む場合に表示されます。

このようなテストは Local Hero の場合もあります。遅いボックスまたは過負荷のCIサーバーで実行すると失敗するためです。

Wait and Seeアンチパターンを The Sleeper と混同しないでください。

19
npellow

不適切に共有されたフィクスチャ-Tim Ottinger
テストフィクスチャ内のいくつかのテストケースは、セットアップ/分解を使用しないか、必要としません。新しいテストフィクスチャを作成する開発者の慣性が原因の1つです。テストケースをもう1つパイルに追加する方が簡単です。

17
Gishu

巨人

テスト対象のオブジェクトを有効にテストしていますが、数千行にまたがり、多くのテストケースを含むことができる単体テスト。これは、テスト対象のシステムが God Object (James Carrの投稿)であることを示す指標になります。

これの確実な兆候は、数行以上のコードにまたがるテストです。多くの場合、テストは非常に複雑であるため、独自のバグや不安定な動作のバグが含まれるようになります。

16
Gishu

いくつかの点滅するGUIが表示されたら信じます
「実際のユーザーのように」GUIを介してアプリをテストすることに不健康な固執/執着

GUIを介したビジネスルールのテストは、ひどい形式の結合です。 GUIを使用して数千のテストを作成し、GUIを変更すると、数千のテストが中断します。
むしろ、GUIを使用してGUIのみをテストし、それらのテストを実行するときに、GUIを実際のシステムではなくダミーシステムに結合します。 GUIを使用しないAPIを介してビジネスルールをテストします。 -ボブ・マーティン

「見ることは信じることを理解しなければなりませんが、信じることは見ることも知っている必要があります。」- デニス・ウェイトリー

15
Gishu

スリーパー、別名ベスビオ山-ニック・ペロー

将来の特定の日時に失敗する予定のテスト。これは、多くの場合、DateまたはCalendarオブジェクトを使用するコードをテストする際の境界チェックが正しくないために発生します。真夜中などの特定の時刻に実行すると、テストが失敗する場合があります。

「The Sleeper」は「 Wait And See 」アンチパターンと混同しないでください。

そのコードは2000年よりずっと前に置き換えられます-1960年の多くの開発者

14
npellow

枯れ木

スタブが作成されたが、実際にはテストが作成されなかったテスト。

私は実際に生産コードでこれを見ました:

class TD_SomeClass {
  public void testAdd() {
    assertEquals(1+1, 2);
  }
}

私はそれについてどう考えるべきかさえ知りません。

11
Reverend Gonzo

カッコウ-フランク・カーバー
[。独自に作成します。
高度な症状: 不適切に共有されたフィクスチャ

11
Gishu

今日はこれで少し困りました:

ウェットフロア
テストはどこかに永続化されるデータを作成しますが、テストは終了してもクリーンアップされません。これにより、テスト(同じテスト、または場合によっては他のテスト)がsubsequentテスト実行で失敗します。

私たちの場合、テストは「temp」ディレクトリにファイルを残し、テストを最初に実行したユーザーからの許可があります。別のユーザーが同じマシンでテストしようとしたとき:ブーム。ジェームズ・カーのサイトでのコメントの中で、ジョアキム・オールロッジはこれを「ずさんな労働者」と呼び、それは「寛大な残り物」のインスピレーションの一部でした。私の名前のほうが好きです(in辱的でなく、より親しみやすい)。

11
Zac Thompson

40フィートポールテスト

テストしようとしているクラスに近づきすぎるのを恐れて、これらのテストは、チェックするロジックから数え切れないほどの抽象化レイヤーと数千行のコードで隔てられた距離で動作します。そのため、それらは非常に脆く、関心のあるクラスとの間の壮大な旅で発生するあらゆる種類の副作用の影響を受けやすくなっています。

10
bhumphreys

チューリングテスト

あまりにも賢い半分のデータフロー分析を使用して、テスト対象のクラスから収集された多数の多くのアサートを持つ、いくつかの高価なツールによって自動的に生成されるテストケース。開発者は、コードが十分にテストされているという誤った自信を抱かせ、高品質のテストを設計および維持する責任から解放されます。マシンがあなたのためにテストを書くことができるなら、なぜそれは指を引き出してアプリ自体を書くことができないのですか!

ハローバカ。-世界で最も賢いコンピューターから新しい見習い(古いAmigaコミック).

10
bhumphreys

環境破壊者

さまざまな「要件」に対して環境変数/ポートを使用および設定し、その環境に波及し始める「ユニット」テスト。これらのテストのうち2つを同時に実行すると、「使用不可ポート」例外などが発生します。

これらのテストは断続的に行われ、開発者は「もう一度実行するだけ」のようなことを言っています。

私が見た解決策の1つは、使用するポート番号をランダムに選択することです。これにより、競合の可能性は減りますが、明らかに問題は解決しません。そのため、可能な場合は常にコードをモックして、共有できないリソースを実際に割り当てないようにします。

10
gcrain

シークレットキャッチャー-フランクカーバー
アサーションがないため、一見テストを行っていないように見えるテスト。しかし、「悪魔は細部に宿る」..テストは実際にスローされる例外に依存しており、テストフレームワークが例外をキャプチャしてユーザーに失敗として報告することを期待しています。

[Test]
public void ShouldNotThrow()
{
   DoSomethingThatShouldNotThrowAnException();
}
10
Gishu

Doppelgänger

何かをテストするには、テスト対象のコードの一部を同じ名前とパッケージの新しいクラスにコピーし、クラスパスマジックまたはカスタムクラスローダーを使用して最初に表示されるようにする必要があります(コピーが選択されるようにするため)アップ)。

このパターンは、テストでは制御できない、不健全な非表示の依存関係を示しています。

私は彼の顔を見ました...私の顔!それは鏡のようなものでしたが、私の血を凍らせました。

9
Aaron Digulla

母鶏-フランク・カーバー
実際のテストケースが必要とする以上のことを行う一般的なセットアップ。たとえば、テストが何かの有無のみをアサートする場合に、明らかに重要で一意の値が設定されたあらゆる種類の複雑なデータ構造を作成します。
高度な症状: 不適切に共有されたフィクスチャ

それが何をするのか分かりません...念のため、とにかくそれを追加しています。-匿名の開発者

7
Gishu

すべてのテスト

これがこれまで言及されていなかったとは信じられませんが、テストは単一責任原則を破るべきではありません。

私はこれに何度も遭遇しましたが、このルールを破るテストは、定義上、維持するのが悪夢です。

7
thegreendroid

ラインヒッター

一見すると、テストはすべてをカバーし、コードカバレッジツールは100%でそれを確認しますが、実際には、テストは出力分析なしでコードにヒットするだけです。

coverage-vs-reachable-code

6

結合双子

人々が「ユニットテスト」と呼んでいるが、依存関係(ファイル構成、データベース、サービス、つまり、テストでテストされていない、人々が怠gotで隔離されていない部分)から分離されていないため、実際には統合テストです。スタブ化またはモック化されるべき依存関係が原因で失敗します。

0
PositiveGuy