89

私は MVCStoreFront アプリで Rob Connery の Web キャストを見ていました。

public Decimal DiscountPrice
{
   get
   {
       return this.Price - this.Discount;
   }
}

次のようなテストがあります。

[TestMethod]
public void Test_DiscountPrice
{
    Product p = new Product();
    p.Price = 100;
    p.Discount = 20;
    Assert.IsEqual(p.DiscountPrice,80);
}

私はすべて単体テストに賛成ですが、この形式のテスト ファースト開発が本当に有益かどうか疑問に思うことがあります。たとえば、実際のプロセスでは、コードの上に 3 ~ 4 層があります (ビジネス リクエスト、要件ドキュメント、アーキテクチャ ドキュメント) 、実際に定義されたビジネス ルール (割引価格は価格 - 割引) が誤って定義されている可能性があります。

その場合、単体テストは何の意味もありません。

さらに、単体テストは別の障害点です。

[TestMethod]
public void Test_DiscountPrice
{
    Product p = new Product();
    p.Price = 100;
    p.Discount = 20;
    Assert.IsEqual(p.DiscountPrice,90);
}

今、テストに欠陥があります。明らかに単純なテストでは大したことではありませんが、複雑なビジネス ルールをテストしているとしましょう。ここで何を得るのですか?

保守開発者がアプリケーションを保守している 2 年間、アプリケーションの寿命を早送りします。ビジネスがルールを変更し、テストが再び失敗し、一部の新人開発者がテストを誤って修正しました...ここで、別の障害点が発生しました。

私が見ているのは、より多くの可能性のある障害点だけであり、実際に有益なリターンはありません.割引価格が間違っていても、テストチームは問題を見つけます.ユニットテストはどのように作業を節約しましたか?

ここで何が欠けていますか?今のところ、TDD を有用なものとして受け入れるのに苦労しているので、TDD を愛するように教えてください。プログレッシブであり続けたいので、私もそうしたいのですが、それは私には意味がありません。

編集: テストが仕様の強制に役立つと何人かの人々が言及し続けています。多くの場合、仕様も間違っていたというのが私の経験ですが、仕様を書くべきではない人によって仕様が書かれている組織で働く運命にあるのかもしれません。

4

17 に答える 17

63

まず、テストはセキュリティのようなものです。テストを 100% 確実に行うことはできませんが、各レイヤーによって信頼性が高まり、残っている問題をより簡単に修正するためのフレームワークが追加されます。

次に、テストをサブルーチンに分割して、それ自体をテストすることができます。20 個の同様のテストがある場合、(テスト済みの) サブルーチンを作成すると、メイン テストはサブルーチンの 20 個の単純な呼び出しであり、これは正しい可能性がはるかに高くなります。

第三に、 TDDはこの問題に対応していると主張する人もいます。つまり、20 個のテストを作成して合格した場合、それらが実際に何かをテストしていると完全に確信できるわけではありません。しかし、作成した各テストが最初に失敗し、その後修正した場合は、それが実際にコードをテストしていることを確信できます。私見ですが、このやり取りには無駄に時間がかかりますが、それはあなたの懸念に対処しようとするプロセスです。

于 2008-10-28T18:38:40.330 に答える
39

テストが間違っていても、本番コードが壊れることはほとんどありません。少なくとも、テストをまったく受けないよりも悪くはありません。したがって、これは「障害点」ではありません。製品が実際に機能するために、テストが正確である必要はありません。機能するものとしてサインオフする前に、それらは正しい必要があるかもしれませんが、壊れたテストを修正するプロセスによって、実装コードが危険にさらされることはありません。

テストは、このような些細なテストであっても、コードが何をすべきかというセカンド オピニオンと考えることができます。1 つの意見はテストであり、もう 1 つは実装です。彼らが同意しない場合は、問題があることがわかり、詳しく調べます。

また、将来誰かが同じインターフェースを最初から実装したい場合にも役立ちます。割引が何を意味するかを知るために最初の実装を読む必要はありません。また、テストは、あなたが持っているかもしれないインターフェースの記述された説明に対する明確なバックアップとして機能します。

そうは言っても、あなたは時間をトレードオフしています。これらの簡単なテストをスキップして節約した時間を使用して作成できる他のテストがある場合、それらはより価値があるかもしれません。実際には、テストのセットアップとアプリケーションの性質に依存します。割引がアプリにとって重要である場合は、とにかく機能テストでこのメソッドのバグを見つけます。単体テストでは、アプリが統合されてエラーの場所がわかりにくくなるまで待つのではなく、このユニットをテストしている時点で、エラーの場所がすぐにわかるようにするだけです。

ところで、個人的には、テスト ケースの価格として 100 を使用しません (または、使用する場合は、別の価格で別のテストを追加します)。その理由は、将来、割引はパーセンテージであると考える人がいるかもしれないからです。このような些細なテストの目的の 1 つは、仕様の読み間違いを確実に修正することです。

【編集について】仕様の間違いが失敗点になるのは仕方ないと思います。アプリが何をするべきかわからない場合は、それができない可能性があります。しかし、仕様を反映するようにテストを作成しても、この問題が拡大するわけではなく、単に解決に失敗するだけです。したがって、新しい障害点を追加するのではなく、ワッフルのドキュメントではなく、既存の障害をコードで表すだけです。]

于 2008-10-28T18:55:13.480 に答える
22

私が見ているのは、より多くの可能性のある障害点だけであり、実際に有益なリターンはありません.割引価格が間違っていても、テストチームは問題を見つけます.ユニットテストはどのように作業を節約しましたか?

単体テストは、実際には作業を節約するためのものではなく、バグを見つけて防止するためのものです。それはより多くの仕事ですが、それは正しい種類の仕事です。コードを最小レベルの粒度で考え、所定の入力セットに対して期待される条件下で動作することを証明するテスト ケースを作成します。変数を分離しているため、バグが発生したときに適切な場所を調べることで時間を節約できます。一連のテストを保存して、後で変更を加える必要があるときに何度でも使用できるようにします。

個人的には、ほとんどの方法論は、カーゴ カルト ソフトウェア エンジニアリング(TDD を含む)から削除された多くのステップではないと思いますが、単体テストの利点を享受するために厳密な TDD に準拠する必要はありません。良い部分は残しておいて、あまりメリットのない部分は捨てる。

最後に、「単体テストの単体テストはどのように行いますか? 」というタイトルの質問に対する答えは、そうする必要はないということです。各単体テストは、非常に単純でなければなりません。特定の入力でメソッドを呼び出し、予想される出力と比較します。メソッドの仕様が変更された場合、そのメソッドの単体テストの一部も変更する必要があることが予想されます。これが、非常に低い粒度で単体テストを行う理由の 1 つであり、一部の単体テストのみを変更する必要があります。要件の 1 つの変更に対して多くの異なるメソッドのテストが変更されていることがわかった場合は、十分な粒度レベルでテストしていない可能性があります。

于 2008-10-28T22:36:24.207 に答える
11

単体テストは、ユニット (メソッド) が期待どおりに動作するようにするためのものです。最初にテストを作成すると、コードを作成する前に、何を期待するかを考える必要があります。実行する前に考えることは常に良い考えです。

単体テストは、ビジネス ルールを反映する必要があります。確かに、コードにエラーが発生する可能性はありますが、最初にテストを作成することで、コードを作成する前にビジネス ルールの観点からテストを作成できます。後でテストを書くと、コードがそれをどのように実装しているかを知っていて、意図が正しいことではなく、実装が正しいことを確認したくなるため、説明したエラーにつながる可能性が高くなります。

また、単体テストは、作成する必要があるテストの 1 つの形式 (つまり、最も低いもの) にすぎません。統合テストと受け入れテストも作成する必要があります。後者は、可能であれば顧客が作成して、システムが期待どおりに動作することを確認します。このテスト中にエラーが見つかった場合は、戻ってユニット テスト (失敗したテスト) を作成し、機能の変更をテストして正しく動作するようにしてから、コードを変更してテストに合格します。これで、バグ修正をキャプチャする回帰テストができました。

[編集]

TDD を実行してわかったことはもう 1 つあります。デフォルトで良いデザインを強制します。これは、高度に結合された設計を単独で単体テストすることはほとんど不可能であるためです。TDD を使用して、インターフェース、制御の反転、および依存性注入 (設計を改善し、結合を減らすすべてのパターン) の使用が、テスト可能なコードにとって非常に重要であることを理解するのにそれほど時間はかかりません。

于 2008-10-28T18:48:59.873 に答える
10

どのようにテストをテストしますか? 突然変異テストは、私が個人的に使用した貴重な手法であり、驚くほど良い効果がありました。詳細についてはリンク先の記事を参照し、さらに学術的な参考文献へのリンクを参照してください。ただし、一般的には、ソース コードを変更して (たとえば、「x += 1」を「x -= 1」に変更)、「テストをテスト」します。テストを再実行して、少なくとも 1 つのテストが失敗することを確認します。テストの失敗を引き起こさないミューテーションは、後で調査するためにフラグが立てられます。

包括的に見える一連のテストで 100% の行と分岐をカバーできることに驚かれることでしょう。しかも、ソース内の行を根本的に変更したり、コメントアウトしたりすることさえできます。多くの場合、これはすべての境界ケースをカバーする適切な入力でテストしていないことに帰着します。時にはもっと微妙な場合もありますが、すべてのケースで、どれだけ多くの結果が得られたかに感銘を受けました.

于 2009-01-08T15:22:12.980 に答える
9

テスト駆動開発 (TDD) を適用する場合、テストの失敗から始まります。不要に思えるかもしれないこのステップは、実際には、単体テストが何かをテストしていることを確認するためにここにあります。実際、テストが失敗しない場合、何の価値ももたらさず、さらに悪いことに、何も証明していない肯定的な結果に頼ることになるため、間違った自信につながります。

このプロセスに厳密に従うと、すべての「ユニット」は、最もありふれたものであっても、ユニット テストが作成するセーフティ ネットによって保護されます。

Assert.IsEqual(p.DiscountPrice,90);

テストがその方向に進化する理由はありません-または、あなたの推論に何かが欠けています. 価格が 100 で割引が 20 の場合、割引価格は 80 です。これは不変条件のようなものです。

ここで、ソフトウェアがパーセンテージに基づく別の種類の割引をサポートする必要があるとします。おそらく購入量によっては、 Product::DiscountPrice() メソッドがより複雑になる可能性があります。そして、これらの変更を導入することで、当初の単純な割引ルールが破られる可能性があります。次に、回帰をすぐに検出するこのテストの値が表示されます。


赤 - 緑 - リファクタリング - これは TDD プロセスの本質を思い出すためのものです。

は、テストが失敗したときの JUnit の赤いバーを示します。

は、すべてのテストが成功したときの JUnit プログレス バーの色です。

緑の状態でリファクタリング: 重複を取り除き、読みやすさを改善します。


ここで、「コードの上の 3 ~ 4 層」についてのポイントに対処します。これは、開発プロセスがアジャイルである場合ではなく、従来の (ウォーターフォールのような) プロセスに当てはまります。アジャイルこそが TDD の出発点です。TDD はエクストリーム プログラミングの基礎です。

アジャイルとは、壁を越えて投げかけられる要件ドキュメントではなく、直接的なコミュニケーションに関するものです。

于 2008-10-30T08:33:04.420 に答える
7

単体テストは、複式簿記と非常によく似た働きをします。同じこと (ビジネス ルール) を 2 つのまったく異なる方法で記述します (実稼働コードではプログラムされたルールとして、テストでは単純な代表例として)。両方で同じ間違いを犯す可能性は非常に低いため、両者が互いに同意する場合、間違っている可能性はほとんどありません。

テストはどのように努力する価値があるのでしょうか? 私の経験では、少なくともテスト駆動開発を行っているときは、少なくとも 4 つの方法で:

  • 適切に分離された設計を考え出すのに役立ちます。適切に分離されたコードのみを単体テストできます。
  • いつ完了したかを判断するのに役立ちます。テストで必要な動作を指定しなければならないことは、実際には必要のない機能を構築せず、機能がいつ完成するかを判断するのに役立ちます。
  • これにより、リファクタリングの安全策が提供され、コードがより変更しやすくなります。と
  • これにより、デバッグにかかる​​時間を大幅に節約できますが、これには非常にコストがかかります (従来、開発者は時間の最大 80% をデバッグに費やしているという見積もりを聞いたことがあります)。
于 2008-10-31T23:06:00.287 に答える
5

ほとんどの単体テスト、テストの仮定。この場合、割引価格は価格から割引を差し引いたものでなければなりません。あなたの仮定が間違っているなら、あなたのコードも間違っているに違いありません。ばかげた間違いを犯した場合、テストは失敗し、それを修正します。

ルールが変更された場合、テストは失敗しますが、それは良いことです。したがって、この場合もテストを変更する必要があります。

原則として、テストがすぐに失敗した場合 (そしてテスト ファースト デザインを使用していない場合)、テストまたはコードのいずれかが間違っています (または、うまくいかない場合は両方)。常識に基づいて (そして仕様によっては可能性があります) 問題のあるコードを修正し、テストを再実行します。

ジェイソンが言ったように、テストはセキュリティです。そして、そうです、欠陥のあるテストが原因で余分な作業が発生することがあります。しかし、ほとんどの場合、それらは大幅な時間の節約になります。(そして、テストを破った人を罰する絶好の機会があります(私たちはラバーチキンについて話している))。

于 2008-10-28T18:46:32.843 に答える
4

できることはすべてテストしてください。メートルをフィートに変換するのを忘れるなどの些細な間違いでさえ、非常に高価な副作用をもたらす可能性があります。テストを書き、それをチェックするコードを書き、それを通過させ、先に進みます。将来のある時点で、誰かが割引コードを変更する可能性があります。テストで問題を検出できます。

于 2008-10-28T18:42:59.620 に答える
4

単体テストと製品コードは共生関係にあると考えています。簡単に言えば、一方が他方をテストします。どちらも開発者をテストします。

于 2010-05-17T11:17:51.193 に答える
3

I see your point, but it's clearly overstated.

Your argument is basically: Tests introduce failure. Therefore tests are bad/waste of time.

While that may be true in some cases, it's hardly the majority.

TDD assumes: More Tests = Less Failure.

Tests are more likely to catch points of failure than introduce them.

于 2009-07-02T22:04:36.487 に答える
3

欠陥が開発サイクルを通じて存続するにつれて、欠陥を修正するコストが (指数関数的に) 増加することに注意してください。はい、テスト チームが欠陥を検出する可能性はありますが、(通常) その時点から欠陥を切り分けて修正するには、単体テストが失敗した場合よりも多くの作業が必要になります。実行する単体テストがありません。

それは通常、些細な例よりも何かを見た方が簡単です...そして些細な例では、ユニットテストを何らかの形で台無しにすると、それをレビューしている人がテストのエラーまたはコードのエラーをキャッチするか、または両方。(それらは見直されていますよね?) tvanfosson が指摘するように、単体テストは SQA 計画の一部にすぎません。

ある意味で、単体テストは保険です。すべての欠陥を発見できる保証はありません。多くのリソースをそれらに費やしているように見えることもありますが、修正可能な欠陥を発見した場合は、はるかに少ない費用で済みます。テストがまったくなく、下流ですべての欠陥を修正しなければならなかった場合よりも。

于 2008-10-28T19:24:17.837 に答える
1

私はこの質問に答える良い方法について少し考えました、そして科学的方法との類似点を描きたいと思います。IMO、あなたはこの質問を言い換えることができます、「あなたはどのように実験を実験しますか?」

実験は、物理的な宇宙についての経験的な仮定(仮説)を検証します。単体テストは、呼び出すコードの状態または動作に関する仮定をテストします。実験の妥当性について話すことはできますが、それは他の多くの実験を通して、何かが合わないことを知っているからです。それは収束的妥当性経験的証拠の両方を持っていません。実験の妥当性をテストまたは検証するための新しい実験を設計することはありませんが、完全に新しい実験を設計することはできます。

したがって、実験のように、単体テスト自体に合格するかどうかに基づいて単体テストの有効性を説明することはありません。他の単体テストとともに、テス​​トしているシステムについて私たちが行う仮定について説明します。また、実験と同様に、テスト対象からできるだけ複雑さを取り除こうとします。 「可能な限りシンプルですが、それ以上シンプルではありません。」

実験とは異なり、収束的妥当性だけでなく、テストが有効であることを検証するためのトリックがあります。テストで検出する必要があることがわかっているバグを巧みに導入し、テストが実際に失敗するかどうかを確認できます。(現実の世界でそれができれば、この収束的妥当性に依存することははるかに少なくなります!)これを行うためのより効率的な方法は、テストを実装する前にテストが失敗するのを監視することです(赤、緑、リファクタリングの赤いステップ) )。

于 2009-07-02T19:11:03.827 に答える
1

テストを作成するときは、正しいパラダイムを使用する必要があります。

  1. 最初にテストを書くことから始めます。
  2. 彼らが最初から失敗することを確認してください。
  3. それらを通過させます。
  4. コードをチェックインする前にコードをレビューします (テストがレビューされていることを確認してください)。

常に確信できるわけではありませんが、テスト全体が改善されます。

于 2010-05-17T11:08:46.167 に答える
1

ここではさらに自動化が役立ちます。はい、単体テストを作成するのは大変な作業になる可能性があるため、いくつかのツールを使用してください。.Net を使用している場合は、Microsoft の Pex のようなものを見てください。コードを調べることで、一連の単体テストが自動的に作成されます。コードのすべてのパスをカバーしようとして、適切なカバレッジを提供するテストが表示されます。

もちろん、コードを見ただけでは、実際に何をしようとしているのかを知ることはできないため、それが正しいかどうかはわかりません。ただし、興味深いテスト ケースが生成されるので、それらを調べて、期待どおりに動作しているかどうかを確認できます。

さらに進んでパラメーター化された単体テストを作成すると (これらは実際にはコントラクトと考えることができます)、これらから特定のテスト ケースが生成されます。今回は、テストでのアサーションが失敗するため、何か問題があるかどうかを知ることができます。

于 2009-03-16T08:08:07.327 に答える
0

コードをテストしなくても、本番環境でユーザーによって確実にテストされます。ユーザーは非常に創造的で、ソフトをクラッシュさせようとし、重大ではないエラーを見つけようとします。

本番環境でバグを修正することは、開発段階で問題を解決するよりもはるかにコストがかかります。副作用として、顧客の流出により収入が失われます。怒っている顧客 1 人に対して、11 人の顧客を失ったか獲得できなかったと考えてよいでしょう。

于 2009-08-04T06:47:03.760 に答える