36

私は最近、単体テストを多用するチームに入りました。なぜこの形式のテストがそれほど重要なのか、誰も私に説明できませんが、彼らはそれを法律のように扱っています.

自動テストの考え方が回帰を防ぐことであることは理解していますが、そもそもそれがどのように問題になるかわかりません。モジュール化されたオブジェクト指向の簡潔なコードで、十分にコメントされている場合、回帰の問題はありません。最初に正しく構築し、将来発生する避けられない機能の追加に備えて設計すれば、テストは必要ありません。

さらに、それは適切なエラー処理とロギングが達成すべきことではないでしょうか? すべての外部依存関係が最初に可用性を再確認できることを確認できるのに、assert ステートメントと単体テストのハッシュ化に何週間も費やす必要はありません。

単体テストは、欠陥があり、不十分に構築された「悪い」コードベースの松葉杖であるという結論に達するのは傲慢ですか?

これは深刻な問題です。どこにも良い答えが見つかりません。自動テストの目的に疑問を呈すると、私が尋ねた人は誰もが私が荒らしだと思うようです.

編集:答えてくれてありがとう、私は今それを理解していると思います。削除に投票した人が何人かいますが、回答してくれた人に感謝したいと思います。それは本当に役に立ちました!

4

9 に答える 9

41

完璧な人はいません。最終的には間違いを犯します。単体テストは、次の目的で、間違いの場所を見つけて特定するように設計されています。

  • あなたが書いたコードの正しさについての信頼を高める
  • リファクタリングの正しさについての信頼を高める
  • テスト段階でバグが導入された場所を追跡するのがはるかに簡単になります

エラー処理とログは、バグがトリガーされた場合にのみ役立ちます。単体テストは、実稼働ではなくテストでバグをトリガーするものです。


次のことを検討してください...

3 つの異なる部分を持つソフトウェアがあり、それぞれに 2 つの異なるオプションがあります。

     A      C      E
    / \    / \    / \
in-<   >--<   >--<   >-out
    \ /    \ /    \ /
     B      D      F

これは、入力を手動で入力して出力をチェックすることでテストできます。最初に、A、C、E をトリガーする入力を入力します。次に、B、D、F まですべてをカバーするまで、A、C、F などを実行したものをいくつか入れます。

ただし、B、D、および F にはそれぞれ、テストする必要がある独自の個別のパラメーターとフローがあることに注意してください。それぞれに 10 のバリエーションがあると言えます。したがって、A、C、E の場合だけ10*10*10 = 1000に、チェックする必要があるのは少なくとも異なる入力です。これらの 6 つのコンポーネントを介して 8 つの異なるフローが考えられるため、8000の異なる入力の組み合わせをチェックして、考えられるすべての異なる入力に確実に到達することを確認する必要があります。

一方、単体テストはできます。各コンポーネントのユニット境界を明確に定義すると、A に対して 10 個、B に対して 10 個というように、それらの境界をテストするユニット テストを作成できます。これにより、コンポーネントの合計 60 の単体テストに加えて、すべてのコンポーネントが適切に結合されていることを確認する少数 (フローごとに 5 つ、つまり 40) の統合テストが得られます。これは、実質的に同じ範囲の機能を達成する合計100のテストです。

単体テストを使用することで、同等のカバレッジを得るために必要なテストの量を約80分の 1 に減らすことができます。これは比較的単純なシステムの場合です。ここで、コンポーネントの数がほぼ確実に 6 を超え、それらのコンポーネントが処理する可能性のあるケースの数がほぼ確実に 10 を超える、より複雑なソフトウェアを考えてみます。単なる統合テストではなく単体テストから得られる節約は、構築を続けます。

于 2012-08-11T20:19:49.853 に答える
23

簡単な答え:はい、あなたは傲慢です。;)

あなたが本当に完璧であると仮定すると、あなたのコードはあなたがそれを書くときに正しくて完璧であるだけでなく、それに課される将来のすべての要件も考慮に入れます。

さて....コードが完璧で正しいことをどうやって知るのですか?あなたはそれをテストする必要があります。テストされていない場合は、それが機能することを信頼することはできません。

リグレッションだけではありません(これは、コードが以前は機能していたことを意味します。機能しなかった場合はどうなりますか?最初に記述されたときはバグがありました)

自動テストの考え方はリグレッションを防ぐことだと理解していますが、そもそもそれがどのように問題になるのかわかりません。十分にコメントされたモジュール式のオブジェクト指向の簡潔なコードには、リグレッションの問題はありません。

誰があなたにそれを言ったの?その人はむち打たれるべきです。オブジェクト指向コードは、他のものと同じようにエラーが発生しやすくなっています。魔法のようなものは何もありません。特効薬ではありません。結局のところ、コードの一部を変更するたびに、どこかで何かを壊す可能性があります。問題のコードに応じて、可能性は大きくなることも小さくなることもありますが、コードがどれほど優れていても、テストしない限り、リグレッションが導入されていないことを確認することはできません。

初めて正しく構築し、将来発生する必然的な機能追加のスラフを設計する場合は、テストは必要ありません。

しかし、最初にどのように正しく構築しますか?上で述べたように、そうするためには、コードが機能することを示すためにテストを行う必要があります。しかし、もっと重要なことは、将来追加される機能をどのように「設計」するのでしょうか。あなたは彼らがまだ何であるかさえ知りません。

さらに、それは、適切なエラー処理とロギングが達成することになっていることではありませんか?すべての外部依存関係が最初にそれらの可用性を再確認することを確認できるのに、なぜアサーションステートメントと単体テストをハッシュ化するのに何週間も費やすのですか?

いいえ、まったくありません。

コードは確かにエラー状態を処理する必要があり、ログに記録する必要があるものを確実にログに記録する必要があります。

しかし、それでも、これがすべて正しく行われることを知っておく必要があります。また、エラー以外の状態も正しく処理されることを知っておく必要があります。「SQLサーバーが利用できない場合は、ユーザーに適切なエラーメッセージを表示して終了する」ことを知っておくと便利です。しかし、それ利用可能である場合はどうなりますか?その場合、アプリケーションは機能しますか?

重要なアプリケーションの場合、うまくいかないことがたくさんあります。多くの機能、多くのコード、そして多くの異なる実行パスがあります。

手動でテストしようとしても、これらすべてのコードパスが実行されることはありません。すべてのコンテキストですべての機能のすべての側面をテストすることは決してありません。そして、たとえそうだったとしても、それは単に「コードは今日機能した」ことを示しています。明日は機能しますか?どうやって知ることができますか?確かに、あなたの直感は「それ以来私がコミットしたコードは何も壊れていない」とあなたに言うかもしれませんが、どうやってそれを知っていますか?もう一度テストする必要があります。そしてまた。そしてまた。

あなたはユニットテストが悪いコードベースの松葉杖であるかどうか尋ねます。それらは反対です。それらは、コードベースが悪くなるのを防ぐ検査であり、医師が訪問します。コードが機能するかどうかだけでなく、いつ機能するか、さらに重要なことに、いつ機能しなくなるかを示します。リグレッションを導入するつもりはないと思いますか?どれくらい確信していますか?あなたは間違っている余裕がありますか?

于 2012-08-11T20:26:15.610 に答える
8

コードを書き始めたばかりのときは、非常に単純で、自動テストは必要ないように思えるかもしれません。

しかし、時間が経つにつれて、コードが大きくなり、要件が変化し、チームが変化します。自動テストの必要性も高まるでしょう。自動化されたテストがなければ、開発者はコードをリファクタリングすることをためらうでしょう。特に、コードを書いたのが開発者でない場合はなおさらです。慎重に設計されたコードであっても、新しい機能を追加すると、既存の機能が壊れる可能性があります。しかし、コードは常に注意深く設計されているわけではありません。実際には、妥協する必要があるかもしれません。さまざまな理由により、一部のコードはクリーンで維持しにくいものになる場合があります。自動化されたテストが必要であることに気付いたときには、一部のクラスはテストが難しい可能性があるため、自動化されたテストを追加できない可能性があります。最初の自動テストを追加するには、最初に大量のコードを書き直すかリファクタリングする必要がある場合があります。

最初からテストを自動化している場合は、コードを自動的にテストできるように設計することが保証されます。

于 2012-08-11T20:19:59.993 に答える
4

私は私の教授の一人、そしてそれを誓う長年のメンターに同じことを尋ねます、彼がここで言及した、または言及された多くの人の一人はこれです:

彼は、テストはコードを文書化するための優れた方法である可能性があると述べました。コードがどのように動作するかをテストで確認したい場合は、複雑さを理解することなく、コードが何をしようとしているのかがわかります。同時に、それが実際にそれを行っているかどうかを確認できるので、それは勝利です... :)

私はコードにコメントするのは好きではないが、自己文書化されたソースコードを好むことを知っていたので、彼はちょうど言ったような気がしますが、それでも興味深い観点です;)

補足として、カバレッジも確認する必要があります。ユニットテストを実行したり、テストスイートを設計したりする場合は、カバレッジを可能な限り100%に近づけてください。つまり、テストでは、すべてのコードを含め、コードの100%がテストされています。さまざまなパスを取ることができ、これは非常に困難であり、テストコードをソースの数倍大きくする可能性がありますが、テストコードを自動化することもできます。簡単な例は、SQLデータベースのテストであり、生成するプログラムを作成できます。考えられるすべてのsqlステートメントとそれらが適切に実行されるテスト。sqlite3には9,100万行を超えるテストがあると思います。http://www.sqlite.org/testing.html非常に興味深いです...ああ、これheも提唱しています。

于 2012-08-11T20:26:28.233 に答える
4

自動テストのアイデアが回帰を防ぐことであることは理解していますが、そもそもそれがどのように問題になるかわかりません。

すでに与えられた良いアドバイスに加えて、回帰は避けられないことを付け加えておきます。業界で働いている期間が短くても (複数のリリース サイクルを経るのに十分な長さ)、さまざまな理由で古いバグが再発する傾向があることに気付きます。

特定の理由の 1 つ: プログラマーは同じ種類の単純な間違いを何度も犯す傾向があります。たとえば、開発者の 1 人がその日付処理コードをリファクタリングしている場合、たとえば 2 月は常に 29 日である、さらに悪いことに、すべての月の長さが同じであるなどの間違った仮定を作り続けます。

回帰テストは、この種のことに対する唯一の防御です。

于 2012-08-11T20:26:48.613 に答える
4

わかりました。私は以前、あなたが今しているように少し考えていたので、あなたの発言のいくつかにコメントし、あなたの発言の別の視点を与えるだけだと思いました. おそらく、これは、テスト駆動開発が実際に行うコーディングの異なるアプローチに光を当てる可能性があります。

自動テストのアイデアが回帰を防ぐことであることは理解していますが、そもそもそれがどのように問題になるかわかりません。

多くの場合、回帰は、以前のコードの動作を十分に理解していない状態で行われた単純な変更ではありませんか? そうである場合、後の段階で誰かが破壊的な変更を行った場合に自動的に検出できるように、すべての開発者がコードの機能を文書化するようにするにはどうすればよいでしょうか? 単体テストはおそらく役立つでしょうか?

モジュール化されたオブジェクト指向の簡潔なコードで、十分にコメントされている場合、回帰の問題はありません。

ドキュメントが非常によく書かれているため、誤解することはほとんど不可能であり、その後のプログラムへの各変更は、十分に熟練したコーダーによって外科的に正確な方法で行われると仮定すると、回帰の問題は発生しない可能性があります。実際、古い動作を変更することは決してありません。

単体テストを動作を文書化する方法と考える場合、それは古いコードの機能を伝達するのに同等またはより効果的な方法ではないでしょうか?

もちろん、テストによって文書化が妨げられることはありませんが、ツールにはそれぞれ長所と短所があります。

最初に正しく構築し、将来発生する避けられない機能の追加に備えて設計すれば、テストは必要ありません。

私の意見では、最初からすべてを正しく柔軟に構築するためのコストと労力は膨大であり、多くの場合、システムが過度に複雑になり、開発ペースが遅くなります。

自問してみてください: 柔軟性のコストはいくらですか? あらゆる問題に対していくつかのソリューションを構築し、それらの間の実行時の切り替えを容易にすることで、リスクを最小限に抑えていますか? それとも、実行時にパッチを適用できるプラグイン システムを構築していますか? これらの戦略は両方とも、開発時間とプロジェクトに複雑さを追加することの両方で、非常に高価です。

今必要なものだけを構築し、必要に応じて修正を迅速にコーディングできるようにチームとして十分な柔軟性を維持しようとすると、実装ごとにどれだけの時間と労力を節約できますか?

既存の機能をテストした場合、機能の拡張は容易になりますか?

さらに、それは適切なエラー処理とロギングが
達成すべきことではないでしょうか? すべての外部依存関係が最初に可用性を再確認できることを確認できるのに、assert ステートメントと単体テストのハッシュ化に何週間も費やす必要はありません。

適切なエラー処理とログ記録はフォールバックとしては良いかもしれませんが、障害回復は正確さの代わりにはなりません。「優雅なエラー処理」と「可用性の再確認」と言うとき、あなたが何を指しているのか正確にはわからないので、これ以上コメントできないと思いますが、これは回復のように聞こえます。エラーが発生しないことは、エラーから回復できることよりもはるかに優れています。

単体テストは、欠陥があり、不十分に構築された「悪い」コードベースの松葉杖であるという結論に達するのは傲慢ですか? これは深刻な問題です。どこにも良い答えが見つかりません。私が自動テストの目的に疑問を呈すると、誰もが私が荒らしだと思うようです.

ドキュメントや適切なコーディングの原則を避けるための言い訳として、単体テストを使用することは確かにできます。これは私が多くの場所で見てきました。しかし、ツールボックスに追加して、製品の正確性、シンプルさ、柔軟性を高める優れたツールでもあります! 新しいチームで頑張ってください。バグを回避するのに役立つ優れたドキュメントやその他の原則について、彼らに 1 つか 2 つ教えることができると思います。また、指数関数的な複雑さの増大を回避しながら、より迅速かつ少ない労力で製品を開発するのに役立ついくつかの原則をおそらく提供してくれると思います。

于 2012-08-11T23:00:42.957 に答える
4

追加するビットはあと 2 つだけです。

  1. 単体テストの最大の利点は、自分のコードのクライアントであるテストを作成する必要があるときに発生する微妙な変化です。テストするのが難しい場合、使用するのは難しいです。私はしばしば、ユーザーの生活をより良くするインターフェースの変更を行います。
  2. 単体テストの作成は、ドキュメントの一種です。それは人々に「これが私のクラスを使用する(そして誤用する)方法です」と伝えます。将来のクライアントには、コードの使用方法に関する組み込みの例がたくさんあるはずです。単体テストはそれらを与えます。
于 2012-08-11T23:21:46.970 に答える
2

モジュール化されたオブジェクト指向の簡潔なコードで、十分にコメントされている場合、回帰の問題はありません。

十分にコメントされていても、コメントは意図した動作を説明するだけです。関数は線形コレクションをソートできますが、クイックソートの代わりにバブルソートを使用します。その時点で、コメントはただのニシンに過ぎません (特に、並べ替えに永遠に時間がかかるという苦情が寄せられた場合に、1 時間後には適切に並べ替えられると主張する場合)。ソート アルゴリズムの元の設計者が、非常に小さなリストでバブルソートを使用し、その後、より大きなリストでクイックソートに切り替えることを意図していた可能性が非常に高いですが、これを検証するテストがなければ、コードを掘り下げずにチェックする方法はありません。ベース。

さらに、コードベースの機能と仕様は、あなたやあなたのショップが何をするかに応じて、1 時間ほどの速さで変更される可能性があります。ドキュメントは、実際の開発に対する大きな障壁となります。より良いのは、回帰がないことを確認するための一連のテスト ケースを用意することです。

最初に正しく構築し、将来発生する避けられない機能の追加に備えて設計すれば、テストは必要ありません。

2 つの言葉: アジャイル開発。 デザインは「ちょうどいい」だけであり、その時点で指定されたものよりも多くの機能を追加することは、YAGNIに非常に大きく違反します。メイン ケースの開発よりも、エッジ ケースの開発により多くの時間を費やすことになります。

さらに、それは適切なエラー処理とロギングが達成すべきことではないでしょうか? すべての外部依存関係が最初に可用性を再確認できることを確認できるのに、assert ステートメントと単体テストのハッシュ化に何週間も費やす必要はありません。

エラー処理は、何か問題が発生したことを示します。単体テストで場所がわかります。適切なテストを行って、コードベースですべてが正常であることを確認することしかできません。

誰でも難読化されたコードを書いて、それが間違いなく正しいと言うことができます。そのコード ベースを継承する人にはわかりません。単体テストは、この問題を軽減するのに役立ちます。

単体テストは、欠陥があり、不十分に構築された「悪い」コードベースの松葉杖であるという結論に達するのは傲慢ですか?

少し。単体テストを必要とするのに、コード ベースが「悪い」ものである必要はありません。変更するだけで済みます。(コード ベースの変更と単体テストを必要としないコードは、同じ次元に存在できません。)

考慮していない別のシナリオは、継承するコード ベースであり、クライアントは、機能が不足しているか、正しく機能していないと主張しています。それは 1 つまたは 2 つの部分である可能性がありますが、いくつかの非常にファンキーな依存関係があります。お気に入りのエディターを持って、大ハンマーでそれをやり遂げることはできません。当分の間機能するコードに影響を与えないことを保証し、新しいコードを修正/実装したい場合は、変更したいモジュールの回帰テストを作成する必要があります。

また、バグが伝播する連鎖が進むほど、バグの修正に費用がかかることもあります。開発中に見つかったバグは、QA で見つかったバグよりも、公開リリースで見つかったバグよりも、リリース候補で見つかったバグよりも修正に費用がかかりません。

ここには、プログラミングのコスト (新規開発ではなく保守コストが負担) や、レガシー コードへの新機能の追加など、他にもいくつかの問題がありますが、ほとんどの場合、そのユニットが非常に重要です。テストが完了します。ある種のユニット リグレッションがないコード ベースで作業している場合、テストでカバーできたはずのバグの修正に多くの時間を費やすことになります。

于 2012-08-11T21:08:33.887 に答える
-1

単体テストを 100% カバーしているため、2 つの異なる視点からコードを実行できます。通常、コードはプログラムの実行時にのみ実行されます。単体テストでは、よりモジュール化された別の方法でフローをテストします。会計における複式簿記を考えてみてください。(私が聞いたところでは、現代の資本主義への礎石です)ここでは、すべてのイベントが2つの異なる元帳に2回表示されます. 最後に、残高はゼロになるはずです。この形式の簿記は、常にそれ自体をチェックしています。これが私が単体テストについて考える方法です。

私が見つけたテストファーストの設計を実行すると、コードが明確になり、より管理しやすくなります。最初にテストを書くことで、クラスが何をすべきかを正確に考えることができます。

于 2012-08-12T21:39:16.840 に答える