web-dev-qa-db-ja.com

「Arrange-Assert-Act-Assert」にする必要がありますか?

Arrange-Act-Assert の古典的なテストパターンに関して、Actに先行する反論を追加することがよくあります。このようにして、通過するアサーションが実際にアクションの結果として通過することを知っています。

私は赤と緑のリファクタリングの赤に似ていると思います。テストの過程で赤いバーを見た場合にのみ、緑のバーは違いをもたらすコードを書いたことを意味することがわかります。合格テストを作成すると、anyコードがそれを満たします。同様に、Arrange-Assert-Act-Assertに関して、最初のアサーションが失敗した場合、Actが最終的なAssertに合格したことを知っているので、実際にはActについて何も検証していません。

テストはこのパターンに従っていますか?なぜですか?

Update明確化:初期アサーションは、本質的に最終アサーションの反対です。 Arrangeが機能したという主張ではありません。 Actがまだ機能していないという主張です。

90
Carl Manaster

以下に例を示します。

_public void testEncompass() throws Exception {
    Range range = new Range(0, 5);
    assertFalse(range.includes(7));
    range.encompass(7);
    assertTrue(range.includes(7));
}
_

単にtrueを返すためにRange.includes()を書いた可能性があります。私はしませんでしたが、私は持っているかもしれないと想像できます。または、他の多くの方法で間違って書いたかもしれません。私はTDDで実際に正しくなったことを期待し、期待します-includes()はちょうど動作しますが、多分私はそうしなかったでしょう。したがって、最初のアサーションは健全性チェックであり、2番目のアサーションが本当に意味があることを確認します。

単独で読むと、assertTrue(range.includes(7));は「変更された範囲に7が含まれているとアサートする」と言っています。最初のアサーションのコンテキストを読んで、「invoke()を呼び出すが含まれることをアサートします。そして、包含しているのはテストしているユニットであるため、一部の(小さな)値。

私は自分の答えを受け入れています。他の多くの人は、私の質問をセットアップのテストに関するものと誤解していました。これは少し違うと思います。

8
Carl Manaster

これは最も一般的なことではありませんが、独自の名前を持つのに十分なほど一般的です。この手法はGuard Assertionと呼ばれます。詳細については、優れた本xUnit Test Patterns Gerard Meszarosによる490ページの詳細な説明をご覧ください(強く推奨)。

通常、私は自分でこのパターンを使用しません。なぜなら、私が保証する必要があると感じる前提条件を検証する特定のテストを書くほうが正しいと思うからです。前提条件が失敗した場合、このようなテストは常に失敗するはずです。これは、他のすべてのテストに組み込む必要がないことを意味します。これにより、1つのテストケースが1つのことのみを検証するため、懸念事項をより適切に分離できます。

特定のテストケースで満たす必要がある多くの前提条件があるため、複数のGuardアサーションが必要になる場合があります。すべてのテストでそれらを繰り返すのではなく、各前提条件に1つ(および1つだけ)のテストを設定すると、テストコードがより維持しやすくなります。

118
Mark Seemann

Arrange-Assume-Act-Assertとして指定することもできます。

次の例のように、NUnitにはこれに対する技術的なハンドルがあります。 http://nunit.org/index.php?p=theory&r=2.5.7

29
Ole Lynge

Arrange-Assert-Act-Assertテストは、常に2つのテストにリファクタリングできます。

1. Arrange-Assert

そして

2. Arrange-Act-Assert

最初のテストは、アレンジフェーズでセットアップされたテストのみをアサートし、2番目のテストは、アクトフェーズで発生したテストのみをアサートします。

これには、失敗したのはアレンジまたはアクトフェーズであるかどうかについてより正確なフィードバックを提供するという利点がありますが、元のArrange-Assert-Act-Assertではこれらが混同され、より深く掘り下げて、どのアサーションが失敗し、失敗したのかを正確に調べる必要があります失敗したのはアレンジか行為かを知るためです。

また、テストをより小さな独立したユニットに分割しているため、ユニットテストの意図をよりよく満たします。

最後に、異なるテストで同様のArrangeセクションが表示された場合は、共有ヘルパーメソッドにこれらを引き出して、テストがより多くなるようにしてください [〜#〜] dry [〜#〜] および将来的に保守しやすくなります。

7
Sammi

Design by Contract に関するWikipediaのエントリをご覧ください。 Arrange-Act-Assert聖三位一体は、同じ概念のいくつかをエンコードする試みであり、プログラムの正確性を証明することに関するものです。記事から:

The notion of a contract extends down to the method/procedure level; the
contract for each method will normally contain the following pieces of
information:

    Acceptable and unacceptable input values or types, and their meanings
    Return values or types, and their meanings
    Error and exception condition values or types that can occur, and their meanings
    Side effects
    Preconditions
    Postconditions
    Invariants
    (more rarely) Performance guarantees, e.g. for time or space used

これを設定するのに費やされる労力とそれが付加する価値の間にはトレードオフがあります。 A-A-Aは、必要な最小限の手順を示す便利なリマインダーですが、追加の手順を作成することを妨げないでください。

1
David Clarke

テスト中のアクションを実行する前に状態を検証するための「健全性チェック」アサーションを投げることは、古い手法です。私は通常、テストが私が期待することを行うことを証明するためにテスト足場として書き、テスト足場でテストが乱雑になるのを避けるために後で削除します。足場を残しておくと、テストが物語として役立つことがあります。

1
Dave W. Smith

このテクニックについてはすでに読んだことがあります-おそらくあなたから-しかし、私はそれを使用しません。主に、ユニットテストでトリプルAフォームに慣れているためです。

今、私は興味があり、いくつかの質問があります:どのようにテストを書きますか、赤緑緑赤リファクタリングサイクルの後にこのアサーションを失敗させますか、それとも後で追加しますか?

おそらくコードをリファクタリングした後、あなたは時々失敗しますか?これは何を意味しますか?たぶん、あなたはそれが助けた例を共有することができます。ありがとう。

1
philant

一般的に、私は「アレンジ、アクト、アサート」がとても好きで、それを私の個人的な基準として使用しています。ただし、アサーションが完了したときにアレンジしたことを整理することは、それを私に思い出させないことの1つです。ほとんどの場合、ほとんどのことはガベージコレクションなどを介して自動的に消えるので、これはそれほど面倒ではありません。ただし、外部リソースへの接続を確立している場合は、完了後にそれらの接続を閉じることができます。アサーションを使用するか、接続を保持しているサーバーまたは高価なリソースまたは他の誰かに与えることができる重要なリソースを持っている人が多くいます。 TearDownまたはTestFixtureTearDownを使用しない開発者の1人 が1つ以上のテストの後にクリーンアップする場合、これは特に重要です。もちろん、「アレンジ、アクト、アサート」は、開いたものを閉じなかった場合の責任を負いません。推奨する「破棄」の「A-Word」シノニムがまだ見つかっていないため、この「落とし穴」についてのみ言及しています。助言がありますか?

1
John Tobler

失敗したテストを調査するときに、これを以前に実行しました。

頭をかなりひっかいた後、「アレンジ」中に呼び出されたメソッドが正しく動作していなかったことが原因だと判断しました。テストの失敗は誤解を招くものでした。アレンジ後にアサートを追加しました。これにより、実際の問題を強調した場所でテストが失敗しました。

テストのアレンジ部分が長すぎて複雑な場合は、コードの匂いもあると思います。

1
WW.

私は今これをやっています。異なる種類のA-A-A-A

Arrange - setup
Act - what is being tested
Assemble - what is optionally needed to perform the assert
Assert - the actual assertions

更新テストの例:

Arrange: 
    New object as NewObject
    Set properties of NewObject
    Save the NewObject
    Read the object as ReadObject

Act: 
    Change the ReadObject
    Save the ReadObject

Assemble: 
    Read the object as ReadUpdated

Assert: 
    Compare ReadUpdated with ReadObject properties

その理由は、ACTがReadUpdatedの読み取りを含まないようにするためです。これは、ACTの一部ではないためです。行為は変更と保存のみです。本当に、アサーション用にARRANGE ReadUpdatedがあり、アサーション用にASSEMBLEを呼び出しています。これは、ARRANGEセクションの混乱を防ぐためです。

ASSERTにはアサーションのみを含める必要があります。これにより、アサートとアサートをセットアップするACTとASSERTの間にASSEMBLEが残ります。

最後に、アレンジで失敗した場合、これらのtrivialバグを防ぐ/見つけるために他のテストがあるはずなので、テストは正しくありません。シナリオiが存在するため、READおよびCREATEをテストする他のテストがすでにあるはずです。 「ガードアサーション」を作成する場合、DRYを壊してメンテナンスを作成している可能性があります。

1
Valamas

例のすべてを本当にテストしたい場合は、次のようなテストを試してください:

public void testIncludes7() throws Exception {
    Range range = new Range(0, 5);
    assertFalse(range.includes(7));
}

public void testIncludes5() throws Exception {
    Range range = new Range(0, 5);
    assertTrue(range.includes(5));
}

public void testIncludes0() throws Exception {
    Range range = new Range(0, 5);
    assertTrue(range.includes(0));
}

public void testEncompassInc7() throws Exception {
    Range range = new Range(0, 5);
    range.encompass(7);
    assertTrue(range.includes(7));
}

public void testEncompassInc5() throws Exception {
    Range range = new Range(0, 5);
    range.encompass(7);
    assertTrue(range.includes(5));
}

public void testEncompassInc0() throws Exception {
    Range range = new Range(0, 5);
    range.encompass(7);
    assertTrue(range.includes(0));
}

それ以外の場合は、エラーの可能性が非常に多くないためです。たとえば、包含後、範囲には7のみが含まれるなどです。範囲内の5つを完全に網羅しようとするための完全に別のテストセット...何を期待しますか?包括的例外、または変更しない範囲?

とにかく、ポイントは、あなたがテストしたい行為に何らかの仮定がある場合、彼ら自身のテストに入れてください、そうですか?

0
Andrew

私はそのようなことをすると思うので、私はそのパターンを使用しません:

Arrange
Assert-Not
Act
Assert

アレンジパーツが正しく動作することを知っていると思われるので、無意味かもしれません。つまり、アレンジパーツにあるものはすべて同様にテストするか、テストを必要としないほどシンプルでなければなりません。

あなたの答えの例を使用して:

public void testEncompass() throws Exception {
    Range range = new Range(0, 5);
    assertFalse(range.includes(7)); // <-- Pointless and against DRY if there 
                                    // are unit tests for Range(int, int)
    range.encompass(7);
    assertTrue(range.includes(7));
}

テスト環境/言語に依存しますが、通常、アレンジ部分の何かが失敗すると、例外がスローされ、テストはアクト部分を開始する代わりに表示に失敗します。いいえ、通常、2番目のAssertパーツは使用しません。

また、Arrangeパーツが非常に複雑で、常に例外がスローされない場合は、メソッド内にラップして独自のテストを作成することを検討することができます。例外をスローします)。

0
schnaader

私が使う:

1. Setup
2. Act
3. Assert 
4. Teardown

クリーンなセットアップが非常に重要だからです。

0
kame