web-dev-qa-db-ja.com

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

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

  • テストを追加することは良い考えですか?
  • もしそうなら、どのようにしてこのようなものから始めますか?

[〜#〜]編集[〜#〜]

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

レガシーシステムのテストを書くことを考えているのは私だけではないと思います。私は、どれだけの時間が費やされ、新しいテストが問題を何回キャッチするか(そして何回それを行わないか)に関するメトリックを保持するつもりです。私は戻ってきて、これから1年かそこらで結果を更新します。

[〜#〜]結論[〜#〜]

したがって、既存のコードに正統性を模倣した単体テストを追加するだけでは基本的に不可能であることがわかります。コードが機能すると、テストを赤信号/青信号にすることはできません。通常、どの動作がテストに重要であるかが明確ではなく、どこから始めるべきかが明確ではなく、終了時に確実に明確ではありません。本当にこの質問をすることでさえ、そもそもテストを書くことの主要なポイントを逃してしまいます。ほとんどの場合、目的の関数を解読してユニットテストをさかのぼって追加するよりも、TDDを使用してコードを書き直す方が実際には簡単であることがわかりました。問題を修正したり、新しい機能を追加したりするときは別の話ですが、これは単体テストを追加するときだと思います(一部は以下で指摘されています)。最終的にほとんどのコードが書き直されます。多くの場合、予想よりも早くなります。このアプローチを採用することで、既存のコードベースの驚くほど大きなチャンクにテストカバレッジを追加することができました。

70
Paul

テストは良い考えですが、元のコーダーがアプリケーションを構築するときにそれらを構築することを意図していましたコードの知識を取得します動作するはずであり、何が壊れるかが予想されます。

このアプローチを採用する場合、壊れる可能性が最も低いテストを記述し、アプリケーションの構築中に発見されたであろうEdgeのケースのほとんどを見落とす可能性が高くなります。

問題は、ほとんどの価値がそれらの「問題」とあまり明白でない状況からもたらされるということです。これらのテストがなければ、テストスイートは実質的にすべての有効性を失います。さらに、回帰の証明が大幅に増えることはないため、会社のアプリケーションには誤った安心感があります。

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

また see

69
FMJaguar

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

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

最初は、コードベースのサイズがごくわずかなテストに比べて圧倒的であるように思われるかもしれませんが、ビッグバンアプローチはありません。最初にsomewhereを作成することは、最善のアプローチについて苦労することよりも重要です。になります。

Michael Feathersの本Working Effectively With Legacy Codeをお勧めします。

35
Danny Woods

すべての単体テストに同じ利点があるわけではありません。単体テストのメリットは、失敗した場合です。失敗する可能性が低いほど、メリットは少なくなります。新規または最近変更されたコードは、本番環境で十分にテストされているまれに変更されたコードよりもバグを含む可能性が高くなります。したがって、新しいコードまたは最近変更されたコードの単体テストは、より有益である可能性が高くなります。

すべての単体テストのコストが等しいわけではありません。今日自分で設計した些細なコードのユニットテストは、だれかがずっと前に設計した複雑なコードよりもはるかに簡単です。また、開発中のテストは通常​​、開発時間を節約します。コスト削減が可能なレガシーコードでは利用できなくなりました。

理想的な世界では、レガシーコードを単体テストする必要がありますが、現実の世界では、ある時点で、レガシーコードに単体テストを追加するコストがメリットを上回ることになります。トリックは、そのポイントを識別することです。バージョン管理は、最後に変更されたコードと最も頻繁に変更されたコードを表示するのに役立ち、ユニットテストを開始することから始めることができます。また、今後変更を加える場合は、それらの変更と密接に関連するコードを単体テストの対象にします。

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

21
Karl Bielefeldt

テストを追加することは良い考えですか?

確かに、コードがクリーンでうまく機能し、最新の手法を使用していて、単体テストがないだけだとは少し難しいと思います。彼らが別のソリューションに座っていないと確信していますか?

とにかく、コードを拡張または保守するつもりなら、真の単体テストはそのプロセスにとって非常に貴重です。

もしそうなら、どのようにしてこのようなものから始めますか?

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

最初にテストするために危険/危険/バイタルなものを選択するのがおそらく最善ですが、特に自分/チームがコードベースやユニットに慣れていない場合は、最初に何かを直接テストして溝に入る方がより効果的かもしれません。テスト。

9
Telastyn

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

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

3
Bernard

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

既存の稼働中のシステムにテストを追加すると、システムが変更されます。つまり、システムがすべて最初からモックを念頭に置いて作成されている場合を除きます。疑わしいですが、モックインターフェイスを挿入できる境界を簡単に定義できるように、すべてのコンポーネントを適切に分離できる可能性は十分にあります。しかし、そうでない場合は、物事をうまく壊す可能性のあるかなり重要な(比較的言えば)変更を行う必要があります。最良のケースでは、これらのテストの作成に多くの時間を費やすことになります。代わりに、詳細な設計ドキュメント、影響分析ドキュメント、またはソリューション構成ドキュメントの多くを作成するのに費やしたほうがよい時間です。結局のところ、それは上司が単体テスト以上のことを望んでいる仕事です。だよね?

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

私は、物事を変更せずに妥当な範囲を提供する外部の自動テストツールに集中します。次に、変更を加えるとき...コードベース内に単体テストの追加を開始できるときです。

3
gbjbaanb

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

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

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

2
Casey