有用な単体テストとは何かを決定する


47

私はphpunitのドキュメントを調べてきましたが、次の引用に出くわしました:

いつでもテストを作成できます。ただし、想像できるテストのほんの一部だけが実際に有用であることがすぐにわかります。あなたがしたいのは、あなたがそれらがうまくいくと思っていても失敗するテスト、またはあなたはそれらが失敗すると思っても成功するテストを書くことです。それを考える別の方法は、費用/利益の観点からです。情報を返してくれるテストを書きたい。-エーリッヒガンマ

不思議に思いました。コスト/ベネフィットについての引用で述べられていることを別にして、ユニットテストが他のテストよりも有用である理由をどのように判断しますか 単体テストを作成するコードの部分をどのように決定しますか?それらの引用の別のものも言ったので、私はそれを求めています:

それで、それがテストに関するものでない場合、それは何についてですか?それはあなたがそれをしようとするために半コックから逃げる前にあなたがしようとしていることを理解することです。動作の小さな側面を簡潔で明確で実行可能な形式で記述する仕様を作成します。とても簡単です。テストを書くということですか?いいえ。コードが何をする必要があるかについての仕様を記述することを意味します。コードの動作を事前に指定することを意味します。しかし、そう遠くはない。実際、コードを記述する直前が最適です。なぜなら、その時点までに必要なだけの情報が手元にあるからです。よくできたTDDと同様に、少しずつ作業を進めます。一度に1つの動作の小さな側面を指定し、それを実装します。すべてが動作を指定することであり、テストを作成することではないことに気付いたら、あなたの視点が変わります。突然、プロダクションクラスごとにテストクラスを持つという考えはとんでもなく制限されます。そして、各メソッドを独自のテストメソッド(1対1の関係)でテストするという考えは笑えるでしょう。-デイブ・アステルス

その重要なセクションは

* そして、各メソッドを(1-1の関係で)独自のテストメソッドでテストするという考えは、笑えるでしょう。*

各メソッドのテストを作成することが「笑える」場合、どのように/いつテストを書くかを選択しますか?


5
それは良い質問ですが、プログラミング言語に依存しない質問だと思います-なぜそれをphpとタグ付けしたのですか?

ここに、私が書くべき単体テストと、自動テストを提供するために重要な領域に関する良い方向性を与えてくれた、本当に良い記事がいくつかあります。- blog.stevensanderson.com/2009/11/04/... - blog.stevensanderson.com/2009/08/24/... - ayende.com/blog/4218/scenario-driven-tests
ケイン

回答:


27

メソッドごとにいくつのテストがありますか?

理論的で非常に非実用的な最大値は、N-Pathの複雑さです(テストはすべて、コードのさまざまな方法をカバーすると仮定します;))。最小値はONE!です。つまり、パブリックメソッドごとに、実装の詳細をテストせず、クラスの外部動作(戻り値と他のオブジェクトの呼び出し)のみをテストします。

あなたは引用する:

*そして、各メソッドを独自のテストメソッド(1対1の関係)でテストするという考えは笑えるでしょう。*

そして尋ねる:

各メソッドのテストを作成することが「笑える」場合、どのように/いつテストを書くかを選択しますか?

しかし、あなたはここで著者を誤解していると思います:

one test methodper を持つという考えはone method in the class to test、著者が「笑える」と呼ぶものです。

(少なくとも私にとって)それは「少ない」ということではなく、「もっと」ということです

だから私が彼を理解したように言い換えさせてください:

そして、各メソッドをONLY ONE METHOD(1-1の関係にある独自のテストメソッド)でテストするという考えは、笑えるでしょう。

見積もりを再度引用するには:

テストを書くのではなく、動作を指定することがすべてだということに気づくと、視点が変わります。


TDDを練習するとき、あなたは考えない

メソッドがあり、メソッドに関するすべてをテストcalculateX($a, $b);するテストtestCalculcateXが必要です。

TDDからわかることは、コードがどのようにすべきかを考えることです。

私は2つの値のうち大きい方を計算する必要があります(最初のテストケース!)が、$ aがゼロより小さければエラーを生成するはずです2番目のテストケース!)そして$ bがゼロより小さければ....(3番目のテストケース!)など。


コンテキストのない単一のメソッドだけでなく、動作をテストする必要があります。

そうすれば、コードのドキュメントであるテストスイートを取得し、実際にそれが何をするのか、おそらくその理由を説明します:)


単体テストを作成するコードの部分をどのように決定しますか?

さて、リポジトリや本番に近い場所で終わるすべてのものにはテストが必要です。上記で述べようとしたので、あなたの引用の著者はそれに反対するとは思わない。

テストを行っていない場合、特に変更を加えていない場合は、コードを変更するのがはるかに難しくなります(より高価になります)。

TDDは、すべてのテストを確実に行う方法ですが、テストを作成する限り問題ありません。あなたは後でそれをするつもりはないので、通常は同じ日にそれらを書くのが役立ちますか?:)



コメントへの応答:

他のメソッドに依存している、または依存しているため、特定のコンテキスト内で適切な量のメソッドをテストすることはできません

これらのメソッドが呼び出すことができるものは3つあります。

他のクラスのパブリックメソッド

他のクラスをモックアウトして、状態を定義します。コンテキストを制御しているので、問題はありません。

* 同じ上の保護されたメソッドまたはプライベートメソッド*

通常、クラスのパブリックAPIの一部ではないものは直接テストされません。

実装ではなく動作をテストし、クラスがすべてを1つの大きなパブリックメソッドまたは呼び出される多くの小さな保護されたメソッドで機能する場合は、implementationです。テストに触れることなく、これらの保護されたメソッドを変更できるようにする必要があります。コードを変更すると振る舞いが変わるとテストが中断するためです!それはあなたのテストが何のためにあるのか、あなたが何かを壊したときにあなたに知らせる:)

同じクラスのパブリックメソッド

それは頻繁に起こりませんか?そして、次の例のようになっている場合、これを処理する方法がいくつかあります。

$stuff = new Stuff();
$stuff->setBla(12);
$stuff->setFoo(14);
$stuff->execute(); 

セッターが存在し、executeメソッドシグネチャの一部ではないことは別のトピックです;)

ここでテストできるのは、間違った値を設定したときにexecuteが爆発するかどうかです。それは、setBla我々はこれら二つの許容値(12&14)はthatsの一つのテストケースよりも、(何らかの理由で)一緒に動作していないことをテストしたい場合は、文字列を渡すときに例外をスローすると、個別にテストすることができますが。

「良い」テストスイートが必要な場合は、PHPで、おそらく@covers Stuff::executeこのメソッドのコードカバレッジのみを生成するために注釈を追加し、セットアップされているその他のものは個別にテストする必要があります(もう一度、あなたはあれが欲しい)。

つまり、最初に周囲の世界をいくつか作成する必要があるかもしれませんが、通常は1つまたは2つの実際の機能だけに及ぶ意味のあるテストケースを作成できるはずです(セッターはここではカウントしません)。残りはエーテルをあざけるか、最初にテストしてから依存することができます(を参照@depends


*注:質問はSOから移行され、最初はPHP / PHPUnitについてでした。そのため、サンプルコードと参照はphpの世界からのものです。テストフレームワーク。


非常に詳細かつ有益な...「コンテキストのない単一のメソッドだけでなく、動作をテストしたい」と言いましたが、特定のコンテキスト内では、他のメソッドに依存または依存しているため、かなりの量のメソッドをテストできません、したがって、有用なテスト条件は、依存関係もテストされている場合にのみコンテキストになりますか?または私はあなたの意味を誤解している>
-zcourts

@ robinsonc494私はこれで行くところを少し良く説明する例で編集します
エドリアン

編集と例のおかげで、確かに役立ちます。私の混乱(あなたがそれを呼ぶことができれば)は、「振る舞い」のテストについて読んだとしても、どういうわけか私は本質的に(おそらく?)実装に焦点を当てたテストケースを考えただけだったと思います。
zcourts

@ robinsonc494たぶんこのように考えてください。誰かをパンチした場合、彼はエーテルでパンチバックするか、警官に電話するか逃げます。その動作。それは人がすることです。彼が彼の脳からのわずかな電荷によって引き起こされる彼のムール貝を使用するという事実は実装です。誰かの反応をテストする場合は、彼をパンチして、期待どおりに動作するかどうかを確認します。あなたは彼を脳スキャナーに入れて、衝動がムール貝に送られるかどうかを見ない。一部はかなりクラスに行く;)
エドリアン

3
テストケースの作成方法を実際にクリックするのに役立ったTDDの最も良い例は、ボブマーティンおじさんのボウリングゲームカタだったと思います。 slideshare.net/lalitkale/bowling-game-kata-by-robert-c-martin
エイミーアヌシェウスキ

2

テストと単体テストは同じものではありません。単体テストは、テスト全体の非常に重要で興味深いサブセットです。ユニットテストの焦点は、上記の引用とやや矛盾する方法で、この種のテストについて考えるようになると主張します。

まず、TDDまたはDTH(つまり、緊密に調和して開発およびテスト)を実行する場合、設計を正しく行うための焦点として、作成したテストを使用します。コーナーケースについて考え、それに応じてテストを記述することで、最初からバグが入らないようにします。実際、合格することを期待するテストを記述します(TDDの開始時に失敗しますが、それは単なる順序付けの人工物です。あなたはコードについて考えたので、私たちは彼らが渡すことを期待し、ほとんどのコードは完了します。

次に、リファクタリングを行うと、ユニットテストが本当に独自になります。実装を変更しますが、答えは変わらないことを期待しています-ユニットテストは、インターフェイスコントラクトを破ることに対する保護です。そのため、テストに合格することを期待しています。

これは、おそらく安定しているパブリックインターフェイスには、すべてのパブリックメソッドがテストされていることを確認できるように、明確なトレーサビリティが必要であることを意味します。

明示的な質問に答えるには:パブリックインターフェイスの単体テストには価値があります。

応答コメントで編集:

プライベートメソッドをテストしますか?はい、そうする必要がありますが、何かをテストする必要がない場合は、妥協します。結局のところ、パブリックメソッドが機能している場合、プライベートなものにあるこれらのバグは非常に重要なのでしょうか?実用的には、解約はプライベートなもので発生する傾向があり、パブリックインターフェイスを維持するために一生懸命働きますが、依存するものが変わるとプライベートなものが変わる可能性があります。ある時点で、内部テストの維持に多大な労力を費やすことがあります。その努力は十分に費やされていますか?


それで、あなたが言っていることを理解するために、テストするときは、パブリックインターフェイスのテストに集中してください。それが正しいと仮定すると、ユニットテストを行っていないプライベートメソッド/インターフェースにバグを残す大きな可能性はありません。本当に失敗したはずです。そう考えるのは間違っていますか?
zcourts

コードカバレッジを使用すると、パブリックメソッドのテスト中にプライベートメソッドのコードが実行されていないことを確認できます。パブリックメソッドが完全にカバーされている場合、カバーされていないプライベートメソッドは明らかに未使用であり、削除できます。そうでない場合は、パブリックメソッドに対してさらにテストが必要です。
デビッドハークネス

2

単体テストは、より大きなテスト戦略の一部である必要があります。私はこれらの原則に従って、どの種類のテストを作成し、いつ実行するかを選択します。

  • エンドツーエンドのテストの作成に焦点を当てます。単体テストよりもテストごとに多くのコードをカバーするので、より多くのテストバンを得ることができます。これらをシステム全体の自動化された自動検証にします。

  • 複雑なロジックのナゲットに関する単体テストの記述にドロップダウンします。ユニットテストは、エンドツーエンドテストのデバッグが困難な場合や、適切なコードカバレッジを作成するのが面倒な場合に、その価値があります。

  • テスト対象のAPIが安定してから、いずれかのタイプのテストを記述します。実装とテストの両方をリファクタリングする必要はありません。

ロブ・アシュトンにはこのトピックに関する素晴らしい記事があり、上記の原則を明確にするために私はそこから大きく引用しました。


+1-必ずしもあなたのすべてのポイント(または記事のポイント)に同意するわけではありませんが、私が同意するのは、盲目的に行われた場合、ほとんどの単体テストは役に立たないということです(TDDアプローチ)。ただし、単体テストの実行に時間をかける価値があるものを賢く決定する場合、これらは非常に貴重です。より高いレベルのテスト、特にサブシステムレベルの自動化されたテストを書くことで、はるかに大きな価値を得ることに完全に同意します。エンドツーエンドの自動化されたテストの問題は、サイズ/複雑さがあるシステムの場合、完全に非実用的ではないにしても難しいことです。
ダンク

0

私は、うまく機能していると思われる単体テストに対する別のアプローチに従う傾向があります。ユニットテストを「いくつかの動作をテストする」と考えるのではなく、「コードが従わなければならない仕様」と考える。このようにして、オブジェクトが特定の方法で動作する必要があることを基本的に宣言できます。また、プログラムの他の場所で、比較的バグがないと確信できると仮定できます。

パブリックAPIを作成している場合、これは非常に貴重です。ただし、通常、100%のユニットテストカバレッジに近づくことは価値がなく、ユニットテスト方法で「テスト不能」と見なされるものを見逃すため、エンドツーエンドの統合テストも常に必要になります(モック、等)

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