テストのテスト方法は?


53

コードをテストして、コードをより正確にします(実際、不正確になる可能性低くなります)。ただし、テストはコードでもあり、エラーが含まれることもあります。また、テストにバグがある場合は、コードが改善されることはほとんどありません。

テストで考えられる3種類のエラーを考えることができます。

  1. プログラマーが目前のタスクを誤解し、テストが実行すべきだと思ったとおりにテストを実行する場合の論理エラー。これは誤りです。

  2. 基礎となるテストフレームワークのエラー(例えば、漏れやすいモックの抽象化)。

  3. テストのバグ:テストは、プログラマーが考えていることとは少し異なっています。

タイプ(1)のエラーを防ぐことは不可能と思われます(プログラマーが...賢くならない限り)。ただし、(2)および(3)は扱いやすい場合があります。これらのタイプのエラーにどのように対処しますか?それらを避けるための特別な戦略はありますか?たとえば、テスト作成者の前提条件のみをチェックする特別な「空の」テストを作成しますか?また、壊れたテストケースのデバッグにどのように取り組みますか?


3
私がモックについて読んだすべての紹介記事は、この問題にぶつかりそうです。モックを開始すると、テストは常にテスト対象のコードよりも複雑になります。明らかに、これは実際のコードをテストする場合には当てはまりませんが、学習しようとしているときは非常に落胆します。
Carson63000

@ Carson63000それがテスト済みのモックで何かをテストする単純なテストである場合、複雑さは分割され、制御されます(私は思う)。
mlvljr

13
しかし、テストテストをどのようにテストしますか?
オコド

+1。項目1は要件エラーである可能性があります。要件を確認することによってのみ防止できます。要件分析者でもない限り、おそらくプログラマーの
手に負えない

@ocodo:ウォッチャーを見るのと同じ方法。:)
グレッグブルクハルト

回答:


18

テストはすでにテストされています。テストではコードと期待値の違いのみが検出されるため、テストはバグから保護されています。問題がある場合、エラーが発生します。エラーはコード内にあるか、テストで同じ確率で発生する可能性があります。

  1. コードとテストの両方に同じバグを追加できないようにするテクニックがいくつかあります。

    1. クライアントは、実装者とは異なる人でなければなりません。
    2. 最初にテストを作成し、次にコードを作成します(テスト駆動開発など)。
  2. 基盤となるプラットフォームをテストする必要はありません。テストは、ユーザーが作成したコードを実行するだけでなく、プラットフォームからもコードを実行します。テストプラットフォームでバグをキャッチする必要はありませんが、プラットフォームのバグを常に隠すコードやテストを書くのは非常に困難です。つまり、テスト/コードとプラットフォームで、作成するテストごとに確率が低下します。これを実行しようとしても、非常に難しいタスクになります。

  3. テストにバグがある場合もありますが、テストは開発されたコードによってテストされるため、通常は簡単に発見されます。コードとテストの間には、自己執行フィードバックがあります。どちらも、インターフェイスの特定の呼び出しがどのように動作するかについて予測を行います。応答が異なる場合、コードにバグがある必要はありません。テストにもバグがある可能性があります。


とてもいい答えです。私は、テストとコードの間の自己補強ループのアイデアと、基盤となるプラットフォームのバグを一貫して隠すテストを書くのが難しいという観察が好きです。
リサードゾパ

実際のコードが何をすべきかについての欠陥のある仮定に基づいてテストが作成されるのを防ぎません。これにより、テスト中に非常に厄介なバグが検出されないままになることがあります。それを防ぐ唯一の方法は、実際のコードを書いている組織とはまったく関係のない第三者によってテストが書かれていることです。そうすることで、要件文書の解釈に関してお互いの考えを「汚染」できません。
jwenting

24

個々のテストをできるだけ小さく(短く)してみてください。

これにより、そもそもバグが作成される可能性が低くなります。作成したとしても、簡単に見つけることができます。単体テストは小規模で具体的で、障害や偏差に対する許容度が低いと想定されています。

結局のところ、それはおそらく経験の問題でしょう。より多くのテストを作成すればするほど、より上手になればなるほど、安っぽいテストを作成する必要が少なくなります。


3
テストにかなり複雑なセットアップが必要な場合はどうなりますか?時々、これらの種類のものはあなたのコントロール下にありません。
リサードゾパ

まあ、私は複雑なセットアップがテストの「初期条件」であると推測しています。それが失敗した場合、すべてのテストが失敗するはずです。実際、私は今そのようなプロジェクトに取り組んでおり、ユニットテストを使用したことがない人は常に同じことを尋ねました。ユニットテストが実際に何であるかを説明するまで:)プロジェクトの複雑さ。
ハンニバルレクター博士

この「初期条件」が満たされていることを確認する最良の方法は、まさに私の質問のポイントです。そのために別のテストを作成しますか?または、この条件が当てはまらない場合、テストが中断すると仮定しますか?セットアップが「壊滅的」に悪くなく、わずかにずれている状況はどうですか?
リサードゾパ

2
初期条件が正しくない場合、テストは失敗するはずです。それがポイントです。状態Aにあるとき、結果を期待しますB。stateがない場合A、テストは失敗します。その時点で、失敗した理由、不適切な初期条件、または不適切なテストを調査できますが、どちらの場合でも失敗するはずです。あなたが言うようにあっても、それは、ある「ややオフ」(すなわち"A" => "B""a" => "b"しかし、決して"a" => "B"あなたのテストが悪いではありませんか)。
博士ハンニバルレクター

19

1つの方法は、テストするコードの前にテストを記述し、正しい理由で最初にテストが失敗するようにすることです。TDDを使用する場合は、少なくともこのレベルのテストをテストする必要があります。

テストスイートの品質をテストするより包括的な方法は、突然変異テストを使用することです。


2
そして、あなたのテストが正しい理由で失敗すること
フランクシェラー

@フランク-はい。それを答えに追加します。
ドンロビー

そして、あなたはテストされる新しい振る舞いのための新しいテストを追加しています。既存のテストに追加しないでください。
Huperniketes

@DonRoby、実際に突然変異テストが有用だと感じましたか?テストケースであなたが見つけたどのような不備はありますか?
dzieciou

4

#1および#3の場合:ユニットテストにはロジックを含めないでください。そうする場合、おそらくユニットテストで複数のことをテストしていることになります。単体テストのベストプラクティスの1つは、単体テストごとにテストを1つだけにすることです。

ユニットテストの作成方法の詳細については、Roy Osheroveによるこのビデオをご覧ください


広告#3-テストは可能な限りシンプルであり、ロジックを含めるべきではないことに同意します。ただし、必要なオブジェクトを作成するときは、テストのセットアップフェーズについて考えてください。わずかに間違ったオブジェクトを作成する可能性があります。これは私が考えている種類の問題です。
リサードゾパ

「少し間違ったオブジェクト」と言うとき、オブジェクトの状態が正しくない、またはオブジェクトの実際の設計が正しくないということですか?オブジェクトの状態については、おそらく妥当性をチェックするテストを作成できます。設計が間違っている場合、テストは失敗するはずです。
ピアズマイヤーズ

3

#1に関して言えば、この側面については、コードレビューとペア設定することをお勧めします。前提条件を立てたり、物事を間違えたりするのは簡単ですが、テストが何をしているのか、ポイントは何かを説明する必要がある場合、間違ったターゲットに照準を合わせている場合は拾いやすくなります。


2

単体テストの試行を停止するポイントが必要です。いつ線を引くか知っている必要があります。テストケースをテストするためにテストケースを作成する必要がありますか?テストテストケースに書き込まれた新しいテストケースについてはどうですか?それらをどのようにテストしますか?

if (0 > printf("Hello, world\n")) {
  printf("Printing \"Hello, world\" failed\n");
}

編集:コメントで提案された説明で更新されました。


-1何?これには関連性がないようです。
代替

2
単体テストの試行を停止するポイントが必要です。いつ線を引くかを知っている必要があります。テストケースをテストするためにテストケースを作成する必要がありますか?テストテストケースに書き込まれた新しいテストケースについてはどうですか?それらをどのようにテストしますか?
aufather

2
Process Brainは、ステートメントを外挿しようとしているときにEInfiniteRecursionを発生させました...
Mason Wheeler

回答をコメントに置き換えると、+ 1が表示されます
自己への注意-名前を考えてください

3
すべての公平において、あなたの例はストローマンです。printf()を呼び出す実際のプログラムではなく、Cライブラリのprintf()サブシステムをテストしています。ただし、ある時点で、テストの再帰テストを中断する必要があることに同意します。
ティムポスト

2

ねえ。
アプリケーションが必要です:

  • あなたの製品
  • その製品のテスト。

製品に対してテストを実行しているとき、実際にはテスト自体に興味がなく、製品とテストの間の相互作用に興味があります。テストが失敗しても、アプリケーションにバグがあるとは言いません。製品とテストの相互作用は成功しなかったと言ってます。今、何が悪いのかを判断するのはあなたの仕事です。次のいずれかです。

  • アプリケーションは期待どおりに動作していません(この期待はテストで表されています)
  • アプリケーションが正しく動作している、あなたはちょうどあなたのテストでこの動作を正しく文書化していない

私にとって、テストの失敗は単純なフィードバックではなく、あれこれが間違っているということです。これは矛盾があることを示す指標であり、両方を調べて、問題が発生したかどうかを確認する必要があります。最後に、アプリケーションが正しいことを確認する責任があります。テストは、チェックする価値のある領域を強調するための単なるツールです。

テストでは、アプリケーションの一部のみをチェックしています。アプリケーションをテストし、テストをテストします。


2

テストは、バグを隠すほど「スマート」であってはなりません。

記述しているコードは、一連の仕様を実装しています。(Xの場合はY、Zの場合はQ、など)。テストは、ZがQの場合を除き、Xが実際にYであると判断することです。これは、テストでXを設定し、Yを検証することだけを意味します。

しかし、それはすべてのケースをカバーしているわけではなく、おそらくあなたは言っているでしょう、そしてあなたは正しいでしょう。しかし、テストを「スマート」にして、XがZではなくYだけでなければならないことを十分に理解している場合、基本的にはテストでビジネスロジックを再実装しています。これは、以下でもう少し詳しく説明する理由から問題があります。最初のテストを「賢く」することでコードカバレッジを改善するべきではなく、代わりにXとZを設定してQを検証する2番目のダムテストを追加する必要があります。ハッピーパスとも呼ばれます)、エッジケースを個別のテストとしてカバーするものです。

これにはいくつかの理由がありますが、何よりもまず、失敗したテストがビジネスロジックのバグによるものか、テストのバグによるものかをどのように判断するのですか?明らかに、答えは、テストが可能な限り単純であれば、バグを抱えている可能性は非常に低いということです。 テストにテストが必要だと思うなら、あなたは間違ってテストしています。

その他の理由としては、要件が変更された場合、努力を複製しているだけです(既に述べたように、単一のテストですべての可能性を発揮するのに十分なスマートテストを書くことは、基本的にテストしようとしているビジネスロジックを複製することです)その後、テストは新しい要件を反映するように簡単に変更でき、テストは一種のドキュメントとして機能します(テスト対象のユニットの仕様が何であるかを言う正式な方法です)。

TL:DR:テストにテストが必要な場合は、間違っています。 ダムテストを書いてください


0

答えではありません(コメントする権限はありません)が、テストケースを開発する他の理由を忘れてしまったのではないかと考えていました
。自動化されたテストスイートは、統合の前に、問題を早期に発見するのに役立ちます。要件への変更は比較的簡単にテストできます。変更が新しいものになり、合格した古いテストケースの変更されたバージョンになる可能性があり、古いケースが失敗を拾い続けるためです。


0

簡単な答え:製品コードはテストをテストします

これを経済学で使用されるクレジット/デビットモデルと比較してください。仕組みは非常に単純です-クレジットが借方と異なる場合、何か間違っています。

同じことがユニットテストにも当てはまります-テストが失敗した場合、何かが間違っていることを示します。実動コードである場合もありますが、テストコードである場合もあります。重要な場合、この最後の部分。

タイプ(1)のバグは単体テストでは検出できないことに注意してください。このタイプのバグを回避するには、他のツールが必要です。

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