コードをテストして、コードをより正確にします(実際には正しくない可能性は低い)。ただし、テストはコードでもあり、エラーが含まれる場合もあります。また、テストにバグがある場合でも、コードが改善されることはほとんどありません。
テストでは、次の3つのタイプのエラーが考えられます。
プログラマーが目の前のタスクを誤解したときの論理エラー、およびテストは彼がやるべきだと思っていたことを実行しますが、これは誤りです。
基礎となるテストフレームワークのエラー(例:リークのあるモックの抽象化);
テストのバグ:テストは、プログラマが考えていることとは少し異なっています。
タイプ(1)のエラーは防止できないようです(プログラマーが...賢くならなければ)。ただし、(2)と(3)は扱いやすい場合があります。これらのタイプのエラーにどのように対処しますか?それらを避けるための特別な戦略はありますか?たとえば、テスト作成者の前提をチェックするだけの特別な「空の」テストを作成しますか?また、壊れたテストケースのデバッグにはどのように取り組みますか?
テストはすでにテスト済みです。テストではコードと期待との違いのみを検出するため、テストはバグから保護されています。問題がある場合はエラーになります。エラーはコード内にあるか、テストで同じ確率で発生する可能性があります。
コードとテストの両方に同じバグを追加できないようにするいくつかの手法があります。
基盤となるプラットフォームをテストする必要はありません。テストは、ユーザーが作成したコードを実行するだけでなく、プラットフォームからもコードを実行します。テストプラットフォームでバグをキャッチする必要はありませんが、プラットフォームのバグを常に隠すコードとテストを書くことは非常に困難です。つまり、テスト/コードとプラットフォームでは、確率は作成するテストごとに低くなります。これを行おうとしても、非常に困難な作業になります。
テストにバグがある可能性がありますが、テストは開発されたコードによってテストされるため、通常は簡単に発見できます。コードとテストの間には、自己実施フィードバックがあります。どちらも、インターフェイスの特定の呼び出しがどのように動作するかを予測します。応答が異なる場合は、コードにバグがある必要はありません。テストにバグがある可能性もあります。
個々のテストを可能な限り小さく(短く)してください。
これにより、そもそもバグが発生する可能性が低くなります。なんとか作成しても、見つけやすくなります。単体テストは小さく、具体的で、故障や逸脱に対する許容度が低いことが想定されています。
結局のところ、それはおそらく経験の問題です。作成するテストが多ければ多いほど、それが上手になればなるほど、安っぽいテストを実行する必要性が少なくなります。
1つの戦術は、テストするコードの前にテストを記述し、正しい理由で最初にテストが失敗するようにすることです。 [〜#〜] tdd [〜#〜] を使用する場合、少なくともこのレベルのテストのテストを取得する必要があります。
テストスイートの品質をテストするより包括的な方法は、 変異テスト を使用することです。
#1と#3の場合:単体テストにはロジックを含めないでください。ロジックが含まれている場合は、単体テストで複数のことをテストすることになります。ユニットテストのベストプラクティスの1つは、ユニットテストごとに1つのテストのみを持つことです。
Roy Osheroveによるこのビデオをご覧ください 単体テストを適切に作成する方法の詳細については、こちらをご覧ください。
#1に関しては、この側面についてペアリング/コードレビューを行うことをお勧めします。前提条件を作成したり、誤解したりするのは簡単ですが、テストが何をしているかを説明しなければならない場合、要点は、間違ったターゲットを目指している場合に気付く可能性が高くなります。
単体テストをやめなければならない点があるはずです。いつ線を引くかを知っている必要があります。テストケースをテストするためにテストケースを作成する必要がありますか?テストテストケース用に作成された新しいテストケースはどうですか?それらをどのようにテストしますか?
if (0 > printf("Hello, world\n")) {
printf("Printing \"Hello, world\" failed\n");
}
編集:コメントで提案された説明で更新されました。
テストは、バグを隠蔽するほど十分に「スマート」であってはなりません。
作成しているコードは一連の仕様を実装しています。 (Xの場合はY、Zの場合はQ、など)。すべてのテストで達成しようとしているのは、ZがQでない限り、Xが実際にYであることを確認することです。これは、すべてのテストでXを設定し、Yを確認することを意味します。
しかし、それはすべてのケースをカバーしているわけではなく、おそらくあなたは言っているでしょう、そしてあなたは正しいでしょう。しかし、テストを「スマート」にして、XがYでなければZでなければならないことを認識できるようにする場合、基本的にはテストでビジネスロジックを再実装していることになります。これは、以下でもう少し詳しく説明するため、問題があります。最初のテストを「よりスマート」にしてコードカバレッジを改善すべきではありません。代わりに、XとZを設定してQを検証する2番目のダムテストを追加する必要があります。これにより、2つのテストが行われ、1つは一般的なケースをカバーします(時にはハッピーパスとも呼ばれます)、別のテストとしてEdgeケースをカバーするもの。
これにはいくつかの理由があります。何よりもまず、失敗したテストがビジネスロジックのバグによるものか、テストのバグによるものかをどのように判断するかです。明らかにその答えは、テストが可能な限り単純であれば、バグが潜んでいる可能性は非常に低いということです。 テストにテストが必要だと思われる場合は、テストが間違っています。
他の理由としては、要件を変更した場合、努力を複製しているだけです(すでに述べたように、1つのテストですべての可能性を実行するのに十分に賢明なテストを作成すると、基本的に、最初にテストしようとしているビジネスロジックが複製されます)。その場合、テストは新しい要件を反映するように簡単に変更できるはずです。テストは一種のドキュメントとして機能します(テスト対象のユニットの仕様を正式に示す方法です)。
TL:DR:テストにテストが必要な場合、それは間違っています。 ダムテストを書く。
ねえ。
次のアプリケーションが必要です:
製品に対してテストを実行しているとき、実際にはテスト自体ではなく、製品とテストの間のinteractionに関与しています。テストが失敗しても、アプリケーションにバグがあるとは言えません。これは、製品とテストの間の相互作用が成功しなかったと述べています。何が問題かを判断するのはあなたの仕事です。次のいずれかになります。
私にとってテストの失敗は単純なフィードバックではなく、それこれとそれは間違っています。これは不整合があることを示すものであり、私は両方を調べて欲望がうまくいかなかったことを確認する必要があります。最終的に私はアプリケーションが正しいことを確認する責任があります。テストは、チェックする価値のある領域を強調表示するための単なるツールです。
答えではありませんが(コメントする権限がありません)、テストケースを開発する他の理由を忘れていないか疑問に思っていました...
テストのすべてのバグを把握したら、アプリケーションを簡単に回帰テストできます。自動化されたテストスイートは、統合の前に、問題を早期に発見するのに役立ちます。要件の変更は比較的簡単にテストできます。変更は、合格した古いテストケースの新しい変更されたバージョンになる可能性があり、古いケースは失敗を拾い続けるためです。
短い答え:製品コードはテストをテストします。
これを、経済学で使用されているcredit/debitモデルと比較してください。仕組みは非常にシンプルです-貸方が借方と異なる場合は、何か問題があります。
彼はユニットテストでも同じです-テストが失敗した場合、何かが間違っていることを示しています。量産コードかもしれませんが、テストコードかもしれません!この最後の部分は重要です。
タイプ(1)のバグは単体テストでは検出できないことに注意してください。このタイプのバグを回避するには、他のツールが必要です。