ユニットテストの失敗が悪いと見なされるのはなぜですか?


93

一部の組織では、明らかに、ソフトウェアリリースプロセスの一部は単体テストを使用することですが、どの時点でもすべての単体テストに合格する必要があります。たとえば、すべてのユニットテストが緑色で合格していることを示す画面が表示される場合があります。

個人的には、これは次の理由からそうあるべきではないと思います。

  1. これは、コードが完璧であり、バグが存在してはならないという考えを促進します。これは、実世界ではどのようなサイズのプログラムにとっても確かに不可能です。

  2. 失敗する単体テストを考えるのは意欲をくじくものです。または、修正するのが難しい単体テストを確実に考え出してください。

  3. いずれかの時点ですべてのユニットテストが合格した場合、その時点でのソフトウェアの状態の全体像はありません。ロードマップ/目標はありません。

  4. 実装前に、事前に単体テストを書くことを思いとどまらせます。

単体テストに失敗したソフトウェアをリリースすることも、悪いことではないことをお勧めします。少なくとも、ソフトウェアのある側面には制限があることを知っています。

ここに何かが足りませんか?組織がすべての単体テストに合格することを期待するのはなぜですか?これは夢の世界に住んでいますか?そして、それは実際にコードの本当の理解を妨げていませんか?


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
maple_shaft

回答:


270

この質問にはIMHOのいくつかの誤解が含まれていますが、私が注目したい主なものは、ローカルの開発ブランチ、トランク、ステージングまたはリリースブランチを区別しないということです。

ローカルのdevブランチでは、ほぼいつでも失敗する単体テストがある可能性があります。トランクでは、ある程度しか受け入れられませんが、できるだけ早く修正するための強力な指標です。トランクのユニットテストに失敗すると、チームの残りのメンバーを混乱させる可能性があることに注意してください。なぜなら、最新の変更が失敗の原因ではないかどうかを全員が確認する必要があるからです。

ステージングブランチまたはリリースブランチでは、失敗したテストは「レッドアラート」であり、トランクからリリースブランチにマージされたときに、一部の変更セットでまったく問題が発生したことを示します。

単体テストに失敗したソフトウェアをリリースすることも、悪いことではないことをお勧めします。

特定の重大度以下の既知のバグがあるソフトウェアをリリースすることは必ずしも悪いことではありません。ただし、これらの既知のグリッチによってユニットテストが失敗することはありません。そうでない場合は、各ユニットテストの実行後に、20の失敗したユニットテストを調べ、失敗が許容できるものであるかどうかを1つずつ確認する必要があります。これは扱いにくく、エラーが発生しやすく、ユニットテストの自動化の側面の大部分を破棄します。

許容できる既知のバグのテストが本当にある場合は、単体テストツールの無効化/無視機能を使用してください(したがって、デフォルトでは実行されず、オンデマンドでのみ実行されます)。さらに、優先度の低いチケットを課題トラッカーに追加して、問題が忘れられないようにします。


18
これが本当の答えだと思います。OPは「リリースプロセス」と「ビルドサーバーのように聞こえるいくつかの画面[テスト結果を表示]」に言及しています。リリースは開発と同じではありません(本番環境では開発しないでください!)。devでテストに失敗しても問題ありません。TODOのようなものです。ビルドサーバーにプッシュされると、すべて緑色(DONE)になります。
ウォーボ

7
投票数が最も多い回答よりもはるかに優れた回答。理想的な世界の状況について講義することなく、opがどこから来ているのかを理解し、既知のバグ(ロードマップ全体がまれなコーナーケースを修正するためにドロップされるわけではない)の可能性を認識し、ユニットテストは明確にのみ行うべきであることを説明しますリリースブランチ/プロセスで緑色になります。
セバスチャンヴァンデンブローク

5
@SebastiaanvandenBroek:肯定的な返事をありがとう。これを明確にするために:ユニットテストの失敗がトランク内であってもまれである必要があります。
ドックブラウン

4
ここでの問題は、すべての自動テストが単体テストであると考えていることだと思います。多くのテストフレームワークには、失敗すると予想されるテストをマークする機能が含まれています(多くの場合、XFAILと呼ばれます)。(これは、エラー結果を必要とするテストとは異なります。XFAILテストは理想的には成功しますが、成功しません。)テストスイートは、これらの失敗で引き続き合格します。最も一般的なユースケースは、一部のプラットフォームでのみ失敗する(そしてそれらのプラットフォームではXFAILのみである)ものですが、現時点で修正するにはあまりにも多くの作業が必要なものを追跡する機能を使用することも理にかなっています。しかし、これらの種類のテストは通常​​、単体テストではありません。
ケビンキャスカート

1
+1ですが、この文に少し太字で追加することをお勧めします:「これは面倒でエラーが発生しやすく、テストスイートの失敗をノイズとして無視する条件になり、ユニットテストの自動化の側面の大部分を破棄します。
mtraceur

228

...すべての単体テストが緑色で合格-これは良いことになっています。

それはです良いです。それについて「想定される」ことはありません。

これは、コードが完璧であり、バグが存在してはならないという考えを促進します。これは、実世界ではどのようなサイズのプログラムにとっても確かに不可能です。

いいえ。コードをテストしたことと、この時点までテストできることが証明されています。テストがすべてのケースをカバーするわけではない可能性があります。その場合、最終的にバグレポートでエラーが発生するため、[失敗]テストを作成して問題を再現し、テストに合格するようにアプリケーションを修正します。

失敗する単体テストを考えるのは意欲をくじくものです。

失敗したテストまたは否定的なテストは、アプリケーションが受け入れるものと受け入れないものに強い制限を課します。私が知っているほとんどのプログラムは、2月30日の「日付」に反対します。また、私たちである創造的なタイプの開発者は、「彼らの赤ちゃん」を壊したくありません。その結果、「ハッピーパス」の場合に焦点が当てられると、壊れやすいアプリケーションが破損する可能性があります。

開発者とテスターの考え方を比較するには:

  • 開発者は、コードが望むことをするやいなや停止します。
  • テスターは、コードを中断できなくなると停止します。

これらは根本的に異なる視点であり、多くの開発者が調和させるのが難しい視点です。

または、修正するのが難しい単体テストを確実に考え出してください。

自分で作業するためのテストを作成しません。あなたはそれがいることを、あなたのコードは、より重要なのは、やるとすることになって何やっていることを確認するためにテストを書き続けて、行うことになっているものを行うにした後、あなたはその内部実装を変更しました。

  • デバッグは、コードが今日の望みどおりに動作することを「証明」します
  • テストは、コード時間の経過とともに希望どおりに動作することを「証明」します。

いずれかの時点ですべてのユニットテストが合格した場合、その時点でのソフトウェアの状態の全体像はありません。ロードマップ/目標はありません。

唯一の「画像」テストは、テストされた時点でコードが「機能する」スナップショットです。その後の進化は別の話です。

実装前に、事前に単体テストを書くことを思いとどまらせます。

それがまさにあなたがすべきことです。失敗するテストを作成し(テストするメソッドがまだ実装されていないため)、メソッドを機能させるためにメソッドコードを記述し、テストをパスします。これがテスト駆動開発の核心です。

単体テストに失敗したソフトウェアをリリースすることも、悪いことではないことをお勧めします。少なくとも、ソフトウェアのある側面には制限があることを知っています。

壊れたテストでコードをリリースすると、その機能一部が以前のように機能しなくなります。バグを修正したか機能を強化したため、それは意図的な行為である可能性があります(ただし、最初にテストを変更して失敗するようにしてから、修正/強化をコーディングして、テストを進行中にする必要があります)。さらに重要なこと:私たちはすべて人間であり、間違いを犯します。コードを壊す場合、テストを壊す必要があり、それらの壊れたテストは警告音を鳴らします。

これは夢の世界に住んでいますか?

どちらかといえば、それは現実世界に住んでおり、開発者は全知でも不可norでもないこと、間違いを犯すこと、混乱した場合に私たちを捕まえるためのセーフティネットが必要であることを認めています
テストを入力します。

そして、それは実際にコードの本当の理解を妨げていませんか?

おそらく。テストを書くために何かの実装を必ずしも理解する必要はありません(それがポイントの一部です)。テストでは、アプリケーションの動作と制限を定義し、意図的に変更しない限り、それらが同じままであることを確認します。


7
@Tibos:テストを無効にすることは、関数をコメントアウトするようなものです。バージョン管理ができます。これを使って。
ケビン

6
@Kevin「それを使う」とはどういう意味かわかりません。テストを「スキップ」または「保留」、またはテストランナーが使用する規則にマークし、そのスキップタグをバージョン管理にコミットします。
コーキング

4
@dcorking:コードをコメントアウトしないで、削除してください。後で必要と判断した場合は、バージョン管理から復元してください。無効化されたテストのコミットも同様です。
ケビン

4
「テストがすべてのケースをカバーするわけではない可能性があります。」これまでのところ、テストしたコードのすべての重要な部分について、すべてのケースを網羅しているわけでない、と言ってます。
corsiKa

6
@Tibos単体テストの支持者は、失敗したテストを書いてからそのコードを書くまでのサイクル時間は短くすべきだと言っています(例えば20分。30秒と主張する人もいます)。コードをすぐに書く時間がない場合は、おそらくあまりにも複雑です。複雑でない場合は、削除された機能が再び追加された場合に書き換えられる可能性があるため、テストを削除します。コメントアウトしてみませんか?機能再び追加されることを知らないので、コメントアウトされたテスト(またはコード)は単なるノイズです。
CJデニス

32

ユニットテストの失敗が悪いと見なされるのはなぜですか?

そうではありません-テスト駆動開発は、テストの失敗という概念に基づいて構築されています。ユニットテストに失敗して開発を推進し、受け入れテストに失敗してストーリーを推進します。

不足しているのはコンテキストです。単体テストはどこで失敗することを許可されていますか?

通常の答えは、プライベートサンドボックスでのみユニットテストの失敗を許可することです。

基本的な概念は次のとおりです。失敗したテストが共有される環境では、実稼働コードへの変更によって新しいエラーが発生したかどうかを理解するのに余分な労力が必要です。ゼロと非ゼロの違いは、Nと非Nの違いよりも検出と管理がはるかに簡単です。

さらに、共有コードをクリーンに保つことは、開発者が仕事を続けることができることを意味します。私はあなたのコードをマージするとき、私は私がどのように多くのテストの私の理解校正を解決するために支払われています問題からコンテキストをシフトする必要はありませんする必要があり、失敗することを。共有コードがすべてのテストに合格している場合、変更をマージするときに表示されるエラーは、コードと既存のクリーンなベースラインとの相互作用の一部である必要あります。

同様に、搭乗中に新しい開発者は、どのテストが「許容できる」かを発見するのに時間を費やす必要がないため、生産性がより速くなります。

より正確に言うと、規律は、ビルド中に実行されるテストに合格する必要があるということです。

私が知る限り、無効なテストに失敗しても何も問題はありません

たとえば、「継続的インテグレーション」環境では、高いリズムでコードを共有します。多くの場合、統合することは、変更をリリースできる状態にする必要があることを必ずしも意味しません。準備ができるまでトラフィックがコードのセクションに解放されるのを防ぐ、さまざまなダークデプロイテクニックがあります。

これらの同じ手法を使用して、失敗したテストを無効にすることもできます。

ポイントリリースで行った演習の1つは、多くのテストに失敗した製品の開発に対処することでした。私たちが思いついた答えは、スイートを通過して、失敗したテストを無効にし、それぞれを文書化することでした。これにより、有効なすべてのテストに合格するポイントにすばやく到達でき、管理者/目標ドナー/ゴールドオーナーはすべて、そのポイントに到達するために行った取引を確認でき、クリーンアップと新しい作業について十分な情報に基づいた決定を下すことができました。

要するに、実行中のスイートに多数の失敗したテストを残す以外に、実行されていない作業を追跡する他の手法があります。


無効になっいるテストに失敗すると、何も問題はありません」と言うでしょう。
CJデニス

その変化は確かに意味を明確にします。ありがとうございました。
VoiceOfUnreason

26

多くのすばらしい答えがありますが、まだ十分にカバーされていないと思う別の角度を追加したいと思います。テストを行うことのポイントは何ですか。

コードにバグがないことを確認するための単体テストはありません。

これが主な誤解だと思います。これが彼らの役割であるなら、あなたは確かにどこでもテストに失敗することを期待するでしょう。代わりに、

単体テストは、コードが思っているとおりに動作することを確認します。

極端な場合には、既知のバグが修正されていないことを確認することが含まれます。ポイントは、コードベースを制御し、偶発的な変更を避けることです。変更を加えると問題はなく、実際にいくつかのテストに違反することが予想されます。コードの動作を変更しています。壊れたばかりのテストは、変更した内容の細かい軌跡になりました。すべての破損が、変更に必要なものに適合していることを確認します。その場合は、テストを更新して続行します。そうでない場合-新しいコードは間違いなくバグがあります。送信する前に戻って修正してください。

現在、上記のすべては、すべてのテストが緑色である場合にのみ機能し、強い肯定的な結果が得られます。これがまさにコードの動作です。赤いテストにはそのプロパティがありません。「これは、このコードが実行しないことです」が有用な情報になることはめったにありません。

受け入れテストはあなたが探しているものかもしれません。

受け入れテストなどがあります。次のマイルストーンを呼び出すために満たさなければならない一連のテストを書くことができます。これらは設計されたものであるため、赤であっても問題ありません。しかし、それらは単体テストとは非常に異なるものであり、それらを置き換えることも置き換える必要もありません。


2
ライブラリを別のライブラリに置き換える必要がありました。ユニットテストは、すべてのコーナーケースが新しいコードによって依然として同等に扱われることを保証するのに役立ちました。
するThorbjörnRavnアンデルセン

24

私はそれを壊れたウィンドウ症候群に相当するソフトウェアと見なします

動作テストでは、コードの品質が一定であり、コードの所有者がそれを気にしていることがわかります。

品質に注意を払う必要がある場合は、作業しているソースコードのブランチ/リポジトリによって異なります。開発コードには、進行中の作業を示すテストが壊れている場合があります(うまくいけば!)。

稼働中のシステムのブランチ/リポジトリでのテストが壊れると、すぐに警告音が鳴ります。壊れたテストが引き続き失敗することを許可されている場合、またはテストが永久に「無視」とマークされている場合-時間が経つにつれて、その数が徐々に増えることを期待してください。これらが定期的に見直されない場合、壊れたテストが残っていてもよいという先例が設定されています。

多くのショップでは壊れたテストが軽pe的に見られ、壊れたコードをコミットできるかどうかに制限があります


9
テストがシステムのあり方を文書化する場合、それらは必ず合格するはずです。そうでない場合、不変式が壊れていることを意味します。彼らは、システムがされている方法文書ならはずあなたのユニットテストフレームワークは、「既知の問題」としてそれらをマークするための良い方法をサポートしている限り、あなたはアイテムにリンクする場合は-ことを、失敗のテストは同様に彼らの使用を持つことができます課題トラッカーで。どちらのアプローチにもメリットがあると思います。
ルアーン

1
@Luaanはい、これはむしろ、すべての単体テストが等しく作成されていることを前提としています。ビルドマネージャーが、実行時間、脆弱性、その他のさまざまな条件に応じて、何らかの属性を使用してテストを細分化することは確かに珍しいことではありません。
ロビーディー

この答えは、私の非常に自己の経験からすれば素晴らしいです。失敗したテストの束を無視したり、いくつかの点でベストプラクティスを破ったりすることに慣れる人がいると、数か月経つと無視されるテストの割合が劇的に増加し、コード品質が「ハックスクリプト」レベルに低下する。そして、プロセスに全員を思い出すのは非常に難しいでしょう。
USR-ローカルΕΨΗΕΛΩΝ

11

基本的な論理的誤りは次のとおりです。

すべてのテストに合格したときにそれが良ければ、いずれかのテストが失敗してもそれは悪いに違いありません。

ユニットテストでは、それはISすべてのテストに合格したときに良いです。また、テストが失敗した場合にも役立ちます。両者は対立する必要はありません。

失敗したテストとは、ユーザーに届く前にツールによって検出された問題です。それは公開される前に間違いを修正する機会です。そしてそれは良いことです。


興味深い考え方。質問の誤acyは次のように見えます。「単体テストが失敗すると良いので、すべてのテストに合格すると悪いのです」。
ドックブラウン

あなたの最後の段落は良い点ですが、問題は「受け入れられた答えが示すように」「すべてのユニットテストがパスしなければならない任意の時点」とユニットテストのポイントの誤解であるようです。
ダケリング

9

Phill Wの答えは素晴らしいです。交換できません。

しかし、混乱の一部だったかもしれない別の部分に焦点を当てたいと思います。

一部の組織では、明らかに、ソフトウェアリリースプロセスの一部は単体テストを使用することですが、どの時点でもすべての単体テストに合格する必要があります

「いつでも」はあなたのケースを誇張しています。重要なことは、特定の変更が実装された、別の変更の実装を開始する、単体テストに合格することです。
これは、どの変更によってバグが発生したかを追跡する方法です。変更25の実装、変更26の実装前に単体テストが失敗始めた場合、変更25がバグの原因であることがわかります。

変更の実装中に、もちろん単体テストが失敗する可能性があります。tatは、変更の大きさに大きく依存します。ちょっとした微調整以上のコア機能を再開発している場合、新しいバージョンのロジックの実装が完了するまでしばらくテストを中断する可能性があります。


これにより、チームルールに関して競合が発生する可能性があります。私は実際に数週間前にこれに遭遇しました:

  • すべてのコミット/プッシュがビルドを引き起こします。ビルドが失敗することはありません(失敗した場合、またはテストが失敗した場合、コミットしている開発者は非難されます)。
  • すべての開発者は1日の終わりに(不完全であっても)変更をプッシュすることが期待されているため、チームのリーダーは午前中にコードレビューを行うことができます。

どちらのルールでも構いません。ただし、両方のルールを併用することはできません。完了までに数日かかる大きな変更を割り当てられた場合、両方のルールを同時に順守することはできません。毎日私の変更をコメントし、すべてが完了した後にのみコメントを外してコミットしない限り、これは無意味な仕事です。

このシナリオでは、ここでの問題は、単体テストに目的がないということではありません。それはことだ会社は非現実的な期待を持っています。それらのarbitrary意的なルールセットはすべてのケースをカバーしているわけではなく、ルールに従わないことは盲目的にルールの失敗ではなく開発者の失敗と見なされます(私の場合)。


3
これ機能する1つの方法は、ブランチを使用することです。これにより、開発者は、不完全な間はクリーンにビルドする必要のない機能ブランチにコミットしてプッシュしますが、コアブランチへのコミットはビルドをトリガーし、クリーンにビルドする必要があります。
グウィンエヴァンス

1
不完全な変更をプッシュすることを強制するのはばかげています。そうする正当な理由は見当たりません。変更が完了したらコードレビューをしてみませんか?
カラムブラッドベリー

まあ、1つは、ハードディスクが動作を停止したり失われたりする場合、開発者のラップトップ/ワークステーションだけでなく、作業中であってもコミットするポリシーがある場合、コードがそれを確実にする迅速な方法ですリスクのある作業は限られています。
グウィンエヴァンス

1
機能フラグは、見かけ上のパラドックスを修正します。
ラバーダック

1
@Flaterはい、既存のロジックを再加工するためにも。
ラバーダック

6

すべてのユニットテストを修正しないと、壊れたテストを修正する人がいない状態にすぐに移行できます。

  1. 単体テストに合格してもコードが完全であることが示されないため、正しくありません

  2. テストするのも難しいコードを思い付くのは意欲をなくします。これは設計の観点からは良いことです

  3. そこではコードカバレッジが役立ちます(ただし、万能薬ではありません)。また、単体テストはテストの1つの側面にすぎません-統合/受け入れテストも必要です。


6

すでに良い答えにいくつかのポイントを追加するには...

しかし、いつでもすべての単体テストに合格する必要があります

これは、リリースプロセスの理解不足を示しています。テストの失敗は、まだ実装されていないTDDで計画されている機能を示している場合があります。または、将来のリリースで修正が計画されている既知の問題を示している場合があります。または、顧客が気付かない可能性が高いため、これは修正するほど重要ではないと経営陣が判断したものである可能性があります。これらすべてが共有する重要なことは、経営陣が失敗について判断を下したことです。

これは、コードが完璧であり、バグが存在してはならないという考えを促進します。これは、実世界ではどのようなサイズのプログラムにとっても確かに不可能です。

他の答えは、テストの限界をカバーしています。

バグをなくすことがマイナス面だと思う理由がわかりません。チェックしたコードを(可能な限り)配信したくない場合は、想定どおりの処理を行いますが、なぜソフトウェアで作業しているのですか?

いずれかの時点ですべてのユニットテストが合格した場合、その時点でのソフトウェアの状態の全体像はありません。ロードマップ/目標はありません。

ロードマップが必要なのはなぜですか?

単体テストでは、最初に機能が動作することを確認しますが、その後(リグレッションテストとして)誤って何も壊していないことを確認します。既存の単体テストのすべての機能について、ロードマップはありません。すべての機能が動作することがわかっています(テストの範囲内)。そのコードが完成した場合、それ以上の作業の必要がないため、ロードマップはありません。

プロのエンジニアとして、金メッキのtrapを回避する必要があります。趣味の人は、機能するものでエッジをいじくり回す時間を無駄にする余裕があります。専門家として、製品を提供する必要があります。つまり、何かが機能していることを確認し、それが機能していることを確認して、次のジョブに進みます。


6

これは、コードが完璧であり、バグが存在してはならないという考えを促進します。これは、実世界ではどのようなサイズのプログラムにとっても確かに不可能です。

違います。なぜあなたはそれが不可能だと思いますか?動作するプログラムの例:

public class MyProgram {
  public boolean alwaysTrue() {
    return true;
  }

  @Test
  public void testAlwaysTrue() {
    assert(alwaysTrue() == true);
  }
}

失敗する単体テストを考えるのは意欲をくじくものです。または、修正するのが難しい単体テストを確実に考え出してください。

その場合、単体テストではないかもしれませんが、複雑な場合は統合テストです

いずれかの時点ですべてのユニットテストが合格した場合、その時点でのソフトウェアの状態の全体像はありません。ロードマップ/目標はありません。

本当、それは理由のために単体テストと呼ばれ、小さなコード単位をチェックします。

実装前に、事前に単体テストを書くことを思いとどまらせます。

開発者 意志書き込みを抑止任意の彼らはその利点を理解していない場合はテストをその性質により(QAから来た場合を除く)


「開発者は、テストの作成をその性質によって阻止します」-それはまったくナンセンスです。TDDとBDDを実践している開発者の会社全体で働いています。
ラバーダック

@RubberDuck私は問題の「事実」に答えようとしましたが、誇張していました。更新します
user7294900

「XがYの利点を理解していない場合、XはYを実行することを抑止します」は、ほぼすべてのXおよびYに適用されるため、このステートメントはおそらく特に有用ではありません。テストを作成することの利点、具体的には事前に行うことの利点を説明する方がおそらく意味があります。
ダケリング

2
「あらゆるサイズのプログラムには不可能」とは、「サイズに関係なくすべてのプログラム」を意味するのではなく、「重要なプログラム(重要な長さを持たない)」を意味します。重要かつ有用なプログラム。
ベンフォークト

@BenVoigt私は答えとして「重要なプログラム」を与えるとは思わない。
user7294900

4

コードは完璧であり、バグは存在しないという考えを促進します

ほとんど間違いない。それはあなたのテストが失敗するべきではなく、それ以上でもそれ以下でもないという考えを促進します。テスト(多くの場合でも)が「完璧」または「バグなし」について何かを言うと仮定するのは誤りです。テストを浅くするか深くするかを決定することは、良いテストを書くことの重要な部分であり、テストのカテゴリを明確に分ける理由(「ユニット」テスト、統合テスト、キュウリの意味での「シナリオ」など)。

失敗する単体テストを考えるのは意欲をくじくものです。または、修正するのが難しい単体テストを確実に考え出してください。

テスト駆動開発では、コーディングを開始する前に、すべてのユニットテストが最初に失敗することが必須です。この理由から、「赤緑サイクル」(または「赤緑リファクタリングサイクル」)と呼ばれます。

  • テストが失敗しないと、コードがテストによって実際にテストされているかどうかわかりません。2つはまったく関連していない可能性があります。
  • テストを赤から緑に正確に変更するようにコードを変更することで、コードが意図したとおりに機能することを確信でき、それ以上(必要ないかもしれません)になります。

いずれかの時点ですべてのユニットテストが合格した場合、その時点でのソフトウェアの状態の全体像はありません。ロードマップ/目標はありません。

テストはもっと細かい目標です。テスト駆動開発では、プログラマは最初にテスト(単数形)を記述し、次にコードを実装するという明確な目標を持ちます。次のテストなど。

テストの機能は、コードが記述される前に完全に存在することではありません。

エラーメッセージ(例外/スタックトレース)が開発者に作業を実行する必要がある場所を直接示すことができるため、この方法に適した言語およびテストライブラリを使用して正しく実行すると、開発を実際に大幅にスピードアップできます次。

実装前に、事前に単体テストを書くことを思いとどまらせます。

この声明がどうなるかはわかりません。テストの作成は、実装の一部であることが理想的です。

ここに何かが足りませんか?組織がすべての単体テストに合格することを期待するのはなぜですか?

組織はテストがコードに関連することを期待しているためです。成功するテストを作成するということは、アプリケーションの一部を文書化し、アプリケーションがそのテスト(テスト)のとおりに動作することを証明したことを意味します。これ以上でもそれ以下でもありません。

また、テストを行うことの非常に大きな部分は「回帰」です。新しいコードを自信を持って開発またはリファクタリングできるようにします。大量のグリーンテストを行うことで、それが可能になります。

これは、組織レベルから心理レベルに移行します。自分のエラーがテストによってキャッチされる可能性が高いことを知っている開発者は、解決する必要のある問題に対するインテリジェントで大胆なソリューションを思いつくことができます。一方、テストを持っていない開発者は、しばらくすると、変更がアプリケーションの残りの部分を壊すかどうかわからないため、(恐怖のために)停止状態になります。

これは夢の世界に住んでいますか?

いいえ。テスト駆動型アプリケーションでの作業は純粋な喜びです。別の質問で説明できる何らかの理由(「より多くの努力」など)でコンセプトが気に入らない場合を除きます。

そして、それは実際にコードの本当の理解を妨げていませんか?

絶対にそうではない、なぜだろうか?

テストのほかに、テストをソフトウェアのメインドキュメントとして実際に使用する大規模なオープンソースプロジェクト(コードの「理解」とノウハウの管理が非常に差し迫ったトピックである)が多数あります。また、アプリケーション/ライブラリのユーザーまたは開発者向けに、実際に機能する、構文的に正しい例を提供します。これはしばしば見事に機能します。

明らかに、悪いテストを書くことは悪いことです。しかし、それはテスト自体の機能とは関係ありません。


3

(元のコメントから)

必要な機能と将来の目標には違いがあります。テストは、必要な機能に関するものです。正確で、形式的で、実行可能であり、失敗した場合、ソフトウェアは機能しません。将来の目標は、実行可能はもちろんのこと、正確でも正式でもない可能性があるため、問題/バグトラッカー、ドキュメント、コメントなどのように自然言語で残すほうがよいでしょう。

演習として、質問の「ユニットテスト」というフレーズを「コンパイラエラー」(またはコンパイラがない場合は「構文エラー」)に置き換えてみてください。リリースは使用できないため、コンパイラエラーが発生してはならないことは明らかです。しかし、コンパイラエラーと構文エラーは、開発者のマシンでコードを書いているときの通常の状態です。エラーは、終了したときにのみ消えます。そして、それはまさにコードがプッシュされるべき時です。ここで、この段落の「コンパイラエラー」を「ユニットテスト」に置き換えます。


2

自動テストの目的は、できるだけ早く何か壊したことを知らせることです。ワークフローは次のようになります。

  1. 変える
  2. 変更をビルドしてテストします(理想的には自動的に)
  3. テストが失敗した場合、以前に機能していた何かを壊したことを意味します
  4. テストに合格した場合、変更により新しい回帰が導入されなかったことを確信する必要があります(テストの範囲によって異なります)

テストが既に失敗している場合、ステップ3は効果的に機能しません。テストは失敗しますが、調査せずに何かを壊したかどうかはわかりません。失敗したテストの数を数えることもできますが、その場合、変更によって1つのバグが修正されて別のバグが壊れるか、テストが別の理由で失敗し始める可能性があります。これは、すべての問題が修正されるまで、または失敗した各テストが調査されるまで、何かが壊れているかどうかを知る前に、ある程度の時間待つ必要があることを意味します。

ユニットテストが新しく導入されたバグをできるだけ早く見つける能力は、自動テストの最も価値のあるものです。欠陥が発見されない時間が長くなればなるほど、修正に費用がかかります。

それはコードが完璧であるべきであり、バグが存在すべきでないという考えを促進
します。失敗する単体テストを考えることの動機付けです

あなたに何を教えていない動作しないもののためにテスト-物事のためのユニットテスト書き込みを行う仕事をするか、修正しようとしていることを。ソフトウェアに欠陥がないことを意味するのではなく、以前に単体テストを書いた欠陥が再び戻ってこないことを意味します。

事前に単体テストを書くことを思いとどまらせる

それがあなたのために働くなら、テストを前もって書いてください、彼らが合格するまであなたのマスター/トランクにそれらをチェックしないでください。

いずれかの時点ですべてのユニットテストが合格した場合、その時点でのソフトウェアの状態の全体像はありません。ロードマップ/目標はありません。

単体テストはロードマップ/目標を設定するためのものではありません。代わりにバックログを使用するのでしょうか?すべてのテストに合格した場合、「全体像」は、ソフトウェアが破損していないことです(テスト範囲が良好な場合)。よくやった!


2

既存の答えは確かに良いですが、この根本的な誤解に答える人は誰もいません。

どの時点でも、すべての単体テストに合格する必要があります

いいえ。最も確実に、これは真実ではありません。私がソフトウェアを開発している間、NCrunchはほとんどの場合茶色(ビルド失敗)または赤(テスト失敗)のいずれかです。

NCrunchをグリーンにする必要がある(すべてのテストに合格する)のは、ソース管理サーバーにコミットをプッシュする準備ができているときです。その時点で、他の人がコードに依存する可能性があるからです。

これは、新しいテストの作成のトピックにもつながります。テストは、コードのロジックと動作をアサートする必要があります。境界条件、障害条件など。新しいテストを記述するとき、コード内のこれらの「ホットスポット」を特定しようとします。

単体テストは、コードがどのように呼び出されることを期待するかを記録します-前提条件、期待される出力など。

変更後にテストが中断する場合、コードまたはテストにエラーがあるかどうかを判断する必要があります。


サイドノートとして、ユニットテストはテスト駆動開発と連動することがあります。TDDの原則の1つは、壊れたテストがあなたの道しるべであるということです。テストが失敗した場合、テストに合格するようにコードを修正する必要があります。今週初めの具体例を以下に示します。

背景:開発者が使用する、Oracleクエリの検証に使用されるライブラリを作成し、現在サポートしています。クエリが何らかの期待値に一致することをアサートするテストがありました。これにより、大文字と小文字が重要になり(Oracleにはない)、期待値に完全に一致する限り、無効なクエリが完全に承認されました。

代わりに、私のライブラリはAntlrとOracle 12c構文を使用してクエリを解析し、構文ツリー自体にさまざまなアサーションをラップします。有効なもの(解析エラーは発生しなかった)、すべてのパラメーターはパラメーターコレクションによって満たされ、データリーダーによって読み取られたすべての予想される列はクエリに存在します。これらはすべて、さまざまな時期の生産。

同僚のエンジニアの1人が、週末に失敗した(または失敗するはずだったときに成功した)クエリを月曜日に送信しました。私のライブラリは、構文は問題ないが、サーバーがそれを実行しようとしたときに破裂したと言いました。そして、彼がクエリを見たとき、それは明らかに理由でした:

UPDATE my_table(
SET column_1 = 'MyValue'
WHERE id_column = 123;

プロジェクトをロードし、このクエリが無効であることを主張する単体テストを追加しました。明らかに、テストは失敗しました。

次に、失敗したテストをデバッグし、例外をスローすると予想されるコードをステップ実行し、Antlr 前のコードが予期していた方法ではなく、オープンペアレンエラーを発生せていることを把握しました。コードを修正し、テストが現在グリーン(合格)であり、他のプロセスがプロセスで破損、コミット、プッシュされていないことを確認しました。

これにはおそらく20分かかりましたが、その過程で、以前は無視されていたエラーの全範囲をサポートするようになったため、実際にライブラリを大幅に改善しました。ライブラリの単体テストがなかった場合、問題の調査と修正には数時間かかりました。


0

以前の回答から出てくるとは思わない点の1つは、内部テストと外部テストに違いがあるということです(多くのプロジェクトは、2つを区別するのに十分な注意を払っていないと思います)。内部テストでは、一部の内部コンポーネントが正常に機能していることをテストします。外部テストは、システム全体が本来の方法で動作していることを示しています。もちろん、システムの障害につながらないコンポーネントに障害が発生する可能性があります(おそらく、システムが使用しないコンポーネントの機能があるか、システムの障害からシステムが回復する可能性があります)成分)。システム障害を引き起こさないコンポーネント障害は、リリースを停止するべきではありません。

内部コンポーネントのテストが多すぎて麻痺するプロジェクトを見てきました。システムの外部から見える動作を実際に変更せずにコンポーネントの動作を変更しているため、パフォーマンスの改善を試みるたびに、多数のテストが中断されます。これは、プロジェクト全体の俊敏性の欠如につながります。外部システムテストへの投資は、特に非常に低レベルのコンポーネントについて話している場合は特に、内部コンポーネントテストへの投資よりもはるかに良い見返りがあると思います。

失敗した単体テストが実際に重要ではないと示唆するとき、これがあなたが念頭に置いているものであるかどうかと思いますか?おそらく、単体テストの価値を評価し、価値があるよりも多くのトラブルを引き起こすものを捨てる一方で、アプリケーションの外部から見える動作を検証するテストにもっと焦点を当てる必要があります。


「外部テスト」として説明しているものは、他の場所で「統合」テストとして説明されることが多いと思います。
ギャラクティック

はい。ただし、用語の違いに出くわしました。一部の人々にとって、統合テストは展開されたソフトウェア/ハードウェア/ネットワーク構成に関するものですが、私はあなたが開発しているソフトウェアの外部動作について話しているのです。
マイケルケイ

0

「しかし、いつでもすべての単体テストに合格する必要があります」

それがあなたの会社の態度なら、それは問題です。ある特定の時間、つまり、コードが次の環境に移動する準備ができていることを宣言するとき、すべての単体テストに合格する必要があります。しかし、開発中、多くの単体テストが失敗することを定期的に予想する必要があります。

プログラマーが最初の試行で自分の仕事を完璧にすることを期待する合理的な人はいない。私たちが合理的に期待しているのは、既知の問題がなくなるまで彼がそれに取り組み続けることです。

「失敗する単体テストを考え出すのは意欲をそらします。あるいは、修正するのが難しい単体テストを確実に思い付きます。」組織内の誰かが、テストが失敗し、それを修正するためにより多くの作業を引き起こす可能性があるため、可能性のあるテストについて言及すべきではないと考えた場合、その人は仕事に完全に適格ではありません。これは悲惨な態度です。「手術をしているとき、ステッチが正しいかどうかを意図的にチェックしないでください。そうでない場合は、戻ってやり直す必要があります。操作の終了が遅くなりますか?」

コードが実稼働する前にエラーを特定するプログラマーにチームが敵意を抱いている場合、そのチームの態度に本当の問題があります。経営陣が、配達を遅らせるエラーを特定したプログラマーを罰する場合、あなたの会社は破産に向かっている可能性が高いです。

はい、時には合理的な人々が「私たちは期限に近づいています。これは些細な問題であり、それを修正するのに必要なリソースを今すぐ捧げる価値はありません」と言うことは確かに真実です。しかし、あなたが知らないならば、あなたは合理的にその決定をすることができません。エラーのリストを冷静に調べ、優先順位とスケジュールを割り当ててそれらを修正するのは合理的です。問題を故意に無知にして、この決定をする必要がないのは愚かです。あなたが知りたくなかったからといって、顧客が見つけられないと思いますか?


-7

これは確認バイアスの具体例であり、人々は既存の信念を確認する情報を求める傾向があります。

この発生の有名な例の1つは、2,4,6ゲームです。

  • 私の頭には、3つの数字のシリーズが合格または不合格になるというルールがあります。
  • 2,4,6はパスです
  • 3つの数字のセットをリストできますが、それらが合格か不合格かをお知らせします。

ほとんどの人は「1番目と2番目の数字のギャップは2番目と3番目の数字のギャップと同じです」と言うルールを選びます。

彼らはいくつかの数字をテストします:

  • 4、8、12?パス
  • 20、40、60?パス
  • 2、1004、2006?パス

彼らは「はい、すべての観察が私の仮説を確認します、それは真実でなければなりません」と言います。そして、謎を与える人に彼らのルールを発表します。

しかし、彼らは3つの数字のセットに対して単一の「失敗」を受け取ったことはありません。ルールは、実際に持っているすべての情報について、「3つの数字は数字である必要があります」だけでした。

ルールは、実際には数字が昇順であるということだけです。通常、人々は失敗をテストする場合にのみこの謎を正解します。ほとんどの人は、より具体的なルールを選択し、この特定のルールを満たす数値のみをテストすることで、間違っています。

なぜ人々が確認バイアスに陥り、ユニットテストが問題の証拠として失敗するのを見るかもしれないかについて、私よりも確認バイアスを説明できる心理学者が多くいます。自分自身が間違っていることを証明します。


2
それは質問にどのように関連していますか?失敗のユニットテストは、ある定義によって、問題の証拠。
-Frax

1
あなたは絶対にユニットテストすることができ必要がテスト中のシステムが故障モードに入ります。それは、テストが失敗するのを見ないのと同じではありません。また、TDDが「赤
-Caleth
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.