単純な(自己完結型)関数のテストを保持する必要はありますか?


36

このことを考慮:

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

上記の機能に対してさまざまなテストを作成し、自分や他の人に「機能する」ことを証明するとします。

その後、これらのテストを削除して、いつまでも幸せに生きてみませんか?私のポイントは、いくつかの機能が機能することが証明された後、継続的にテストする必要がないことです。私は、これらの機能をまだテストする必要があると述べているカウンターポイントを探しています:...または、はい、これらはテストする必要はありません...


32
コードを変更しない限り、昨日機能していれば明日も機能するというのは、すべての関数に当てはまりませんか?ポイントは、ソフトウェア変更されるということです。
5gon12eder

3
誰もoverride_function('pow', '$a,$b', 'return $a * $b;');彼らの正しい考えで書くことはないだろう...または複雑な数を扱うために書き直そうとするからだ。

8
だから...ええと...それは「バグのないコードであることが証明された」... バグがあります。

このような小さな関数の場合、プロパティベースのテストを検討することをお勧めします。プロパティベースのテストでは、テストケースが自動的に生成され、事前に決定された不変条件のテストが行​​われます。それらは不変式を介してドキュメントを提供します。このような単純な関数の場合、それらは完璧です。
マーティン

6
また、その関数は、(($a*$x + $b)*$x + $c)*$x + $d間違いを起こしやすいが、多くの場合かなり高速なHornerの形式の変更の優れた候補です。変わらないと思うからといって、変わらないわけではありません。
マイケルアンダーソン

回答:


78

回帰試験

それはすべて回帰テストについてです。

次の開発者があなたの方法を見て、あなたが魔法の数字を使っていることに気づいたと想像してください。彼は魔法の数字は悪であると言われたので、彼は2つの定数を作成しました。1つは2つ目の数字、もう1つは3つ目の数字です。彼が既に正しい実装を変更していたということではありません。

気を散らされて、彼は2つの定数を逆にします。

彼はコードをコミットし、すべてが正常に動作するようです。これは、各コミットの後に実行される回帰テストがないためです。

ある日(数週間後になる可能性があります)、何かが他の場所で壊れます。そして、他の場所では、コードベースの完全に反対の場所を意味しますが、これはpolynominal機能とは何の関係もないようです。何時間も苦労してデバッグすると、犯人になります。この間、アプリケーションは本番環境で引き続き失敗し、顧客に多くの問題を引き起こします。

作成した元のテストを保持することで、このような痛みを防ぐことができます。気を取られた開発者はコードをコミットし、ほとんどすぐに何かを壊したことを確認しました。そのようなコードは本番環境には届きません。さらに、ユニットテストはエラーの場所について非常に正確になります。それを解決することは難しくありません。

副作用...

実際、ほとんどのリファクタリングは回帰テストに大きく基づいています。小さな変更を加えます。テスト。合格した場合、すべてが正常です。

副作用は、テストがない場合、実際にはリファクタリングがコードを壊す大きなリスクになることです。多くの場合、管理者にリファクタリングを行うことを説明するのはすでに困難です。以前のリファクタリングの試みで複数のバグが発生した場合、それを行うのはさらに難しくなります。

テストの完全なスイートを使用することにより、リファクタリングを促進し、コードのクリーン化を改善できます。リスクフリーで、定期的にリファクタリングを増やすことは非常に魅力的です。

要件の変更

もう1つの重要な側面は、要件が変わることです。複素数の処理を求められることがありますが、突然、バージョン管理ログを検索して以前のテストを見つけ、それらを復元し、新しいテストの追加を開始する必要があります。

なんでこんな面倒なの?後で追加するためにテストを削除する理由 そもそもそれらを保管しておくことができます。


批判的な注意:リスクがないものはありません。;)
jpmc26

2
回帰は私にとってテストの一番の理由です-あなたのコードが責任について明確で、渡されたものだけを使用している場合(例:グローバルなし)でも、偶然に何かを壊すのは非常に簡単です。テストは完璧ではありませんが、それでも優れています(ほとんどのお客様が非常に不満を抱いている、同じバグが再び発生することはほとんどありません)。テストが機能する場合、メンテナンス費用はかかりません。動作しなくなった場合は、おそらくバグが見つかりました。私は、何千ものグローバルを持つレガシーアプリケーションに取り組んでいます-テストなしでは、必要な変更の多くを敢行しません。
ルアーン

もう1つの注意点:一部の「魔法の」数字は実際には大丈夫であり、それらを定数に置き換えることは悪い考えです。
デュプリケータ

3
@Deduplicatorは知っていますが、特に大学で訓練されたばかりのジュニアプログラマーの多くは、盲目的にマントラに従うことに非常に熱心です。そして「魔法の数字は悪」はその一つです。もう1つは、「1回以上使用されるものはすべてメソッドにリファクタリングする必要がある」ことです。極端な場合は、1つのステートメントしか含まないメソッドが大量に発生します。
-18:11で

@jwenting:MainMaが「この変更を行うのに何も問題はない」と書いたので、そのメモを追加しただけです。

45

バグが発生するほど単純なものはないからです。

あなたのコードは、一見するとバグがないように見えます。実際には、多項式関数の単純なプログラム表現です。

バグがある場合を除き...

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

$xはコードへの入力として定義されておらず、言語またはランタイム、またはスコープによっては、関数が機能しなかったり、無効な結果が生じたり、宇宙船がクラッシュしたりする場合があります。


補遺:

現時点ではコードのバグはないと見なすかもしれませんが、それがどれくらいの期間残っているかを言うのは難しいです。そのような些細なコードのテストを書くことは価値がないと主張されるかもしれませんが、テストをすでに書いているので、作業は完了し、それを削除することは具体的なセーフガードを削除します。

さらに注目すべきは、テストスイートが提供するカバレッジを適切に示すコードカバレッジサービス(coveralls.ioなど)です。コードのすべての行をカバーすることにより、実行するテストの量(品質ではない場合)の適切なメトリックを提供します。多数の小さなテストと組み合わせて、これらのサービスは少なくとも、バグが発生したときにどこを探すべきではないかを教えてくれます。

最終的に、すでにテストを作成している場合は、それを保管してください。削除によるスペースまたは時間の節約は、バグが発生した場合の技術的な負債よりもはるかに少ないためです。


別の解決策があります:非動的で非弱い言語への切り替え。通常、単体テストはコンパイル時の検証が不十分な場合の回避策であり、一部の言語はこのen.wikipedia.org/wiki/Agda_(programming_language)–
Den

21

はい。確実に100%自信を持って言うことができた場合:この関数は編集されず、失敗する可能性のあるコンテキストで実行されることはありません-それが言えれば、テストを削除して、毎回数ミリ秒を節約できますCIビルド。

しかし、できません。または、多くの機能を使用することはできません。そして、満足している信頼しきい値を正確に判断し、特定の関数の不変性と不確実性にどれだけ自信を持っているかを正確に判断するよりも、すべてのテストを常に実行するルールを作成する方が簡単です。

そして、処理時間は安価です。節約されたミリ秒は、何度も乗算されたとしても、すべての関数を尋ねるのに時間をかけることを正当化するのに十分なほど追加されません。


ここで取り上げているのは非常に良い点だと思います。いくつかの小さな機能のテストを維持することについて議論するのに何時間も費やしている2人の男をすでに見ています。
カポール

@Kapol:議論ではなく、どこに行くことができ、どれに留まることができるか、どの基準を使用して決定するか、基準を文書化する場所、最終決定を
誰が承認するかを

12

他の回答で述べられていることはすべて正しいですが、もう1つ追加します。

ドキュメンテーション

ユニットテストは、適切に記述されていれば、開発者に対して、関数が何をするのか、入力/出力の期待値、さらに重要なこととして、どのような動作を期待できるのかを正確に説明できます。

バグを見つけやすくし、混乱を減らすことができます。

誰もが多項式、幾何学、あるいは代数さえ覚えていない:)しかし、バージョン管理にチェックインされた良いユニットテストは私のために覚えているでしょう。

ドキュメントとしてどのように役立つかの例については、Jasmineの概要を参照してください。http://jasmine.github.io/edge/introduction.html 数秒で読み込み、最後までスクロールします。Jasmine API全体が単体テストの出力として文書化されます。

[@Warboからのフィードバックに基づく更新]テストも最新ではないことが保証されます。最新でない場合、テストは失敗し、CIを使用すると一般にビルドエラーが発生します。外部ドキュメントはコードとは無関係に変更されるため、必ずしも最新ではありません。


1
暗黙的に残したドキュメントの一部としてテストを使用することには大きな利点があります:テストは自動的にチェックされます。他の形式のドキュメント、例えば。Webページ上のコードの説明コメントまたはスニペットは、コードが進化するにつれて古くなる可能性があります。ユニットテストが正確でなくなった場合、ユニットテストは失敗を引き起こします。そのジャスミンのページは、この極端な例です。
ウォーボ

DやRustなどの一部の言語では、ドキュメント生成とユニットテストを統合しているため、同じコードを単体テストにコンパイルし、HTMLドキュメントに挿入することができます
Idan Arye

2

リアリティチェック

私は、テストが予算とスケジュールの間の「時間の無駄」であり、顧客がバグに対処すると「品質保証の基本的な部分」であるという挑戦的な環境にいました。

予算があります。あなたの仕事は、その予算でできる限り最高の製品を手に入れることです。「最高」の定義は、一緒に削ることができます(定義するのは簡単な言葉ではありません)。話の終わり。

テストはインベントリ内ツールです。数百万ドル、あるいは数十億ドルを節約する長い歴史を持つ優れたツールあるため、使用する必要があります。機会があれば、これらの単純な関数にテストを追加する必要があります。それはいつかあなたの肌を救うかもしれません。

しかし、現実の世界では、予算とスケジュールの制約により、それは起こらないかもしれません。自分を手続きの奴隷にしないでください。テスト機能は優れていますが、ある時点で、コードよりも単語で開発者ドキュメントを作成する方が多くの工数を費やすことができるため、次の開発者はテストをそれほど必要としません。または、コードベースのリファクタリングに費やした方がよい場合がありますので、難しい獣のように維持する必要はありません。あるいは、上司と予算やスケジュールについて話し合う時間をより多く取って、次の資金調達がパイプラインでやってくるときに彼らが何のために入札しているかをよりよく理解できるようにした方がいいかもしれません。

ソフトウェア開発はバランスです。あなたが時間を過ごすより良い方法がなかったことを確実にするために、あなたがしていることの機会費用を常に数えてください。


4
この場合、すでにテストが行​​われているので、問題はそれらを書くかどうかではなく、ビルドまたはリリースごとにコミットして実行するか、すべてのテストを実行するためのスケジュールです。
パエロエベルマン

0

はい、テストを保持し、実行し続け、合格し続けます。

ユニットテストは、あなた(そして他の人)をあなた(そして彼ら自身)から守るためにあります。

なぜテストを行うのが良い考えなのか。

  • 新しい要件と追加機能に直面して、以前の要件の機能を検証する
  • リファクタリングの演習が正しいことを確認します
  • 内部ドキュメント-これは、コードの使用方法です。
  • 回帰テスト、状況は変わります
    • 変更は古いコードを壊しますか?
    • 変更には、現在の機能またはコードに対する追加の変更要求または更新が必要ですか?
  • テストがすでに作成されている場合、それらを保持します。メンテナンス費用をさらに削減するのは、すでに費やした時間とお金です

2
これは、提供されている他の回答に対して実質的な何かを提供するようには見えません。

1
以前に投稿する際に問題が発生しましたが、振り返ってみると、これは素晴らしい要約です。削除します。
ニール

-1

開発者向けドキュメント

  • (別の開発者として)これがテストされたことをどのようにして知ることができますか?
  • 自己完結型関数のバグを修正したい場合、あなたがすでに検討したバグを導入していないことをどのようにして知ることができますか?
  • 複雑さの指標:テストの数は、何かがどれだけ複雑かを示す適切な尺度になります。これは、成熟して安定しているため、または肥大化して結合しているため、触ってはならないことを示している可能性があります。

ユーザードキュメント

  • 貧しいドキュメントを保留中、{ゼロ、負の値、空のセットなど}が受け入れられるかどうか、期待される戻り値が何かを確認できます。
  • また、オブジェクト/関数の使用方法の良い例を示しています

1
これは、以前の回答で作成され説明されたポイントに対して実質的なものを提供していないようです。「すてきな要約」でさえすでに投稿されています(私の好みでは、それはそれほど素晴らしいことではありませんが、まあまあです)
gnat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.