既存のコードのテストを書く


68

比較的大きなプログラム(C#で900k SLOCなど)があり、すべてが十分にコメント/文書化され、適切に組織化され、適切に機能しているとします。コードベース全体は、もはや会社にいない単一の上級開発者によって作成されました。すべてのコードはそのままテスト可能であり、IoCが全体で使用されます(ユニットテストを作成しなかった奇妙な理由を除く)。今、あなたの会社はコードを分岐させ、変更がコア機能を壊すときを検出するために単体テストを追加したいと考えています。

  • テストの追加は良いアイデアですか?
  • もしそうなら、どのようにしてこのようなものから始めるのでしょうか?

編集

わかりました、それで私は反対の結論に対して良い議論をする答えを期待していませんでした。とにかく問題は私の手に負えないかもしれません。私も「重複した質問」を読みましたが、一般的なコンセンサスは「テストを書くのは良い」ということです...ええ、しかしこの特定のケースではあまり役に立ちません。

レガシーシステムのテストを書くことをここで考えているのは私だけではないと思います。どれだけの時間を費やし、新しいテストが問題をキャッチする回数(および、そうでない回数)のメトリックを保持します。私は戻って来て、私の結果でこれから1年かそこらを更新します。

結論

そのため、正統性に似た既存のコードに単体テストを追加することは基本的に不可能です。コードが動作すると、テストを赤信号/緑信号にすることは明らかにできません。通常、テストする必要がある動作が明確ではなく、どこから始めればよいか、完了したら明確ではありません。本当にこの質問をすることでさえ、そもそもテストを書くことの主要なポイントを見逃しています。ほとんどの場合、意図した機能を解読してユニットテストを遡及的に追加するよりも、TDDを使用してコードを書き直す方が実際に簡単であることがわかりました。問題を修正したり、新しい機能を追加したりするのは別の話であり、ユニットテストを追加するときだと思います(以下で指摘するように)。最終的には、ほとんどのコードが書き直され、多くの場合、予想よりも早くなります。


10
テストを追加しても、既存のコードは壊れません。
ダンピチェルマン

30
あなたは経験したことがありません@DanPichelman schroedinbug「速やかにプログラムをその時点で誰かがソースを読んだり、それが働いていないはずです決して珍しい方法通知でプログラムを使用してまで現われないプログラムでは、設計や実装のバグを、 -修正されるまで全員の作業を停止します。」

8
@MichaelTあなたがそれについて言及したので、私はそれらのうちの1つまたは2つを見たと思います。私のコメントは、「テストを追加しても通常、既存のコードを壊さない」と読むべきでした。ありがとう
ダンピチェルマン

3
リファクタリングまたは変更する予定の事柄についてのみテストを記述してください。
スティーブンエバーズ

3
本「レガシーコードを使用した効果的な作業」を確認してください:amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/…、マイケルフェザーズは、レガシーコードを変更する前にテストを書くことを提案します。
スカラブ

回答:


68

テストは良いアイデアですが、元のコーダーがアプリケーションをビルドするときに、コードがどのように機能するか、そして何が壊れるかについての知識をキャプチャし、それがあなたに転送されるため、テストをビルドするつもりでした

このアプローチを採用する場合、アプリケーションの構築中に発見されるはずのほとんどのエッジケースを逃す可能性が最も低いテストを作成する可能性が高くなります。

問題は、価値のほとんどがこれらの「落とし穴」であり、あまり明らかではない状況に由来することです。これらのテストがなければ、テストスイートは実質的にすべての効果を失います。さらに、同社は、アプリケーションに対するセキュリティに対する誤った認識を持ちます。これは、それがそれほど回帰的な証拠ではないからです。

通常、このタイプのコードベースを処理する方法は、レガシーコードベースが完全にリファクタリングされるまで、新しいコードと古いコードのリファクタリングのテストを記述することです。

参照してください


11
ただし、TDDがなくても、リファクタリングする際には単体テストが役立ちます。
pdr

1
コードが引き続き正常に機能する場合、それは問題ではありませんが、その動作に依存するものを記述するときはいつでも、レガシコードへのインターフェイスをテストすることをお勧めします。
-deworde

1
これが、テストカバレッジを測定する理由です。特定のセクションのテストがすべてのifとelseおよびすべてのエッジケースをカバーしていない場合、そのセクションを安全にリファクタリングすることはできません。カバレッジは、すべての行がヒットしたかどうかを示すため、リファクタリングする前に可能な限りカバレッジを増やすことが目標です。
ルドルフオラー

3
TDDの主な欠点は、コードベースに不慣れな開発者にとってはスイートが実行される可能性があるが、これは誤ったセキュリティ感覚であるということです。BDDは、出力が英語のコードの意図であるため、この点ではるかに優れています。
ロビーディー

3
100%のコードカバレッジは、コードが常に100%正常に動作していることを意味するものではないことを言及したいだけです。メソッドのすべてのコード行をテストできますが、value1で機能するからといって、value2での動作が保証されるわけではありません。
ライアンゼック

35

はい、テストを追加することは間違いなく良い考えです。

あなたはそれがよく文書化されていると言います、そしてそれはあなたを良い地位に置きます。そのドキュメントをガイドとして使用してテストを作成し、重要であるか頻繁に変更される可能性のあるシステムの部分に焦点を当ててください。

最初は、コードベースのサイズはごくわずかなテストと比較して圧倒的に大きいように見えますが、ビッグバンのアプローチはありません。どこかで始めることは、最良のアプローチが何であるかを苦しめることよりも重要です。

優れた詳細なアドバイスについては、Michael Feathersの著書「Working Effectively with Legacy Code」をお勧めします。


10
+1。ドキュメントに基づいてテストを作成する場合、作業コードとドキュメントの間に矛盾があることがわかります。これは非常に貴重です。
カールマナスター

1
テストを追加するもう1つの理由:バグが見つかった場合、将来の回帰テスト用のテストケースを簡単に追加できます。

この戦略は基本的に、レガシーコードに関する章の(無料の)edXオンラインコースCS169.2xで概説されているものです。教師が言うように:本の第9章の「特性テストを使用したグラウンドトゥルースの確立」:beta.saasbook.info/table-of-contents
FGM

21

すべての単体テストに同等の利点があるわけではありません。単体テストの利点は、失敗したときに得られます。失敗する可能性が低いほど、有益ではありません。新規または最近変更されたコードは、実稼働環境で十分にテストされているめったに変更されないコードよりもバグを含む可能性が高くなります。したがって、新規または最近変更されたコードの単体テストは、より有益である可能性が高くなります。

すべての単体テストのコストが等しいわけではありません。他の人がずっと前に設計した複雑なコードよりも、今日自分で設計した些細なコードを単体テストする方がはるかに簡単です。また、開発中のテストは通常​​、開発時間を節約します。従来のコードでは、コストを節約できなくなりました。

理想的な世界では、レガシーコードを単体テストする必要が常にありますが、現実の世界では、ある時点で、レガシーコードに単体テストを追加するコストがメリットを上回ることは理にかなっています。秘Theは、そのポイントを特定することです。バージョン管理は、最後に変更されたコードと最も頻繁に変更されたコードを表示することで役立ち、それらを単体テストの下に置くことから始めることができます。また、今後変更を行う場合は、それらの変更と密接に関連するコードを単体テストの下に置きます。

その方法に従うと、最終的には最も有益な分野でかなり良いカバレッジが得られます。代わりに、収益を生み出すアクティビティを再開する前にユニットテストを実施するために数か月を費やす場合、それは望ましいソフトウェアメンテナンスの決定かもしれませんが、ビジネス上の決定は粗末です。


3
コードがめったに失敗しない場合、実生活では決して起こりえない不可解な問題を見つけるために多くの時間と労力が費やされる可能性があります。一方、コードにバグがあり、エラーが発生しやすい場合は、どこでもテストを開始して、すぐに問題を見つけることができます。
ロビーディー

8

テストの追加は良いアイデアですか?

もちろん、コードがクリーンで適切に動作し、最新の手法を使用しており、単体テストがないことを信じるのは少し難しいと思いますが。彼らは別のソリューションに座っていないのですか?

とにかく、コードを拡張/保守する場合、そのプロセスにとって真の単体テストは非常に貴重です。

もしそうなら、どのようにしてこのようなものから始めるのでしょうか?

一歩ずつ。単体テストに慣れていない場合は、少し学んでください。概念に慣れたら、コードの小さなセクションを1つ選択し、テストを記述します。次に、次、そして次。コードカバレッジは、見逃したスポットを見つけるのに役立ちます。

最初にテストするのは危険な/危険な/生命力のあるものを選ぶのがおそらく最善ですが、最初にグルーブに入るために何かを簡単にテストする方が効果的かもしれません-特にあなた/チームがコードベースやユニットに慣れていない場合テスト。


7
「彼らは別のソリューションに座っていないのですか?」素晴らしい質問です。OPが見落とさないことを願っています。
ダンピチェルマン

残念なことに、アプリケーションは何年も前にTDDが勢いを増したように開始されたため、ある時点でテストを行うことを目的としていましたが、何らかの理由でプロジェクトが開始された後、彼らはそれに到達できませんでした。
ポール

2
価値のない余分な時間がかかっていたので、彼らはおそらくそれに決して到達しませんでした。一人で作業する優秀な開発者は、自動テストを行わなくても、比較的大規模でクリーンで明確で組織化された作業アプリケーションを確実に作成できます。一般に、テストと同様にバグのない迅速な作業が可能です。それはすべて自分の頭の中にあるので、複数の開発者がそれを作成する場合と比較して、バグや組織の問題の可能性が大幅に低くなります。
ベン・リー

3

はい、テストを行うことをお勧めします。既存のコードベースの動作を意図したとおりに文書化し、予期しない動作をキャッチするのに役立ちます。テストが最初に失敗した場合でも、テストを許可し、後でコードをリファクタリングして、パスして意図したとおりに動作するようにします。

小さなクラス(依存関係がなく、比較的単純なクラス)のテストを書き始め、大きなクラス(依存関係があり、より複雑なクラス)に進みます。長い時間がかかりますが、最終的には可能な限りコードベースをカバーできるように、忍耐強く粘り強くしてください。


うまく機能しているプログラムに失敗したテストを本当に追加しますか(OPによると)。
MarkJ

はい、それは何かが意図したとおりに機能していないことを示しており、さらなるレビューが必要だからです。これにより議論が促され、誤解や以前は不明だった欠陥があれば修正することが期待されます。
バーナード

@Bernard-または、テストにより、コードが何をすべきかについての誤解が明らかになる場合があります。事後に書かれたテストは、元の意図を正しくカプセル化しないリスクを伴います。
ダンピチェルマン

@DanPichelman:同意したが、これはテストを書くことをやめさせてはならない。
バーナード

それ以外の場合は、コードが防御的に記述されていないことを示します。
ロビーディー

3

OK、私は反対の意見を述べるつもりです。

既存の動作中のシステムにテストを追加すると、そのシステムが変更されます。ただし、システムはすべて、最初からモックを念頭に置いて記述されています。私はそれを疑いますが、それはあなたがモックインターフェースを滑り込ませることができる簡単に定義可能な境界を持つすべてのコンポーネントの良好な分離を持っている可能性はかなり高いです。しかし、そうでない場合は、物事を壊す可能性のある非常に重要な(相対的に言えば)変更を行う必要があります。最良の場合、これらのテストを書くのに多くの時間を費やすことになります。代わりに、詳細な設計ドキュメント、影響分析ドキュメント、またはソリューション構成ドキュメントのロードを書く方が良いでしょう。結局のところ、それは上司が単体テスト以上にやりたいことです。そうじゃない?

とにかく、ユニットテストは一切追加しません。

私は、外部の自動化されたテストツールに集中して、物事を変えることなく合理的な範囲を提供します。次に、変更を行うとき...コードベース内にユニットテストを追加し始めることができます。


2

私はしばしばこの状況に遭遇し、十分なテストカバレッジまたはテストカバレッジなしで大きなコードベースを継承し、現在は機能の追加、バグの修正などを担当しています。

私のアドバイスは、追加したものを確認してテストすることです。現在のコードでバグを修正したり、ユースケースを変更したりする場合は、テストを作成します。何かに触れる必要がある場合は、その時点でテストを作成します。

これが破綻するのは、既存のコードが単体テスト用に適切に構成されていない場合です。そのため、リファクタリングに多くの時間を費やし、小さな変更のテストを追加できるようにします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.