28

TDD を使用して開発されたプロジェクトはリファクタリングが容易であると聞いたことがあります。これを実践すると、包括的な一連の単体テストが得られるためです。変更によってコードが破損した場合は (うまくいけば) 失敗します。ただし、これについて私が見たすべての例は、リファクタリングの実装を扱っています。たとえば、アルゴリズムをより効率的なものに変更します。

アーキテクチャのリファクタリングは、設計がまだ練られている初期段階でより一般的であることがわかりました。インターフェイスが変更され、新しいクラスが追加および削除され、関数の動作がわずかに変更される可能性もあります (これを行うには必要だと思っていましたが、実際にはそれを行う必要があります)、など...しかし、各テストケースが密結合されている場合これらの不安定なクラスに対して、設計を変更するたびにテスト ケースを常に書き直さなければならないのではないでしょうか?

TDD では、どのような状況でテスト ケースを変更および削除しても問題ありませんか? テスト ケースを変更しても問題が発生しないことをどのように確認できますか? さらに、常に変化するコードと包括的なテスト スイートを同期させるのは大変なことのようです。ソフトウェアが構築され、安定し、機能するようになると、単体テスト スイートがメンテナンス中に非常に役立つことは理解していますが、TDD は早い段階でも役立つはずですが、それはゲームの後半です。

最後に、TDD やリファクタリングに関する優れた本は、この種の問題に対処するものでしょうか? もしそうなら、あなたはどちらをお勧めしますか?

4

8 に答える 8

10

心に留めておく必要があることの 1 つは、TDD は主にテスト戦略ではなく、設計戦略であるということです最初にテストを作成します。これは、より優れた分離設計を考え出すのに役立ちます。また、設計をより適切に分離すると、リファクタリングも容易になります。

クラスやメソッドの機能を変更すると、当然、テストも変更する必要があります。実際、TDD に従うということは、もちろん、最初にテストを変更することを意味します。ほんの少しの機能を変更するために多くのテストを変更する必要がある場合、それは通常、ほとんどのテストが動作を過剰に指定していることを意味します。つまり、テストする必要がある以上のテストを行っています。もう 1 つの問題は、運用コードで責任が十分にカプセル化されていないことです。

いずれにせよ、小さな変更が原因で多くのテストが失敗する場合は、コードをリファクタリングして、将来同じことが起こらないようにする必要があります。それはいつでも可能ですが、その方法が常に明らかであるとは限りません。

設計変更が大きくなると、状況が少し複雑になる可能性があります。はい、新しいテストを作成して古いテストを破棄する方が簡単な場合があります。場合によっては、少なくともリファクタリングされる部分全体をテストするいくつかの統合テストを作成できます。そして、ほとんど影響を受けていない一連の受け入れテストがまだ残っていることを願っています。

まだ読んでいませんが、「XUnit Test Patterns - Refactoring Test Code」という本について良いことを聞きました。

于 2008-11-01T20:41:54.040 に答える
8

さらに、包括的なテストスイートを絶えず変化するコードと同期させる必要があるのは苦痛のようです。ユニットテストスイートは、ソフトウェアが構築され、安定し、機能するようになると、メンテナンス中に非常に役立つ可能性があることを理解していますが、それはゲームの後半であり、TDDも早い段階で役立つはずです。

主要なアーキテクチャの変更が行われているこれらの初期の変更では、単体テストスイートを配置することのオーバーヘッドが感じられることに同意しますが、単体テストを使用することの利点はこの欠点をはるかに上回っていると思います。問題は精神的な問題であることが多すぎると思います。ユニットテストはコードベースの二級市民と考える傾向があり、それらをいじる必要があることに憤慨しています。しかし、時間が経つにつれて、私はそれらに依存し、それらの有用性を評価するようになり、コードベースの他の部分と同様に、それらをそれほど重要ではなく、保守や作業に値するものと考えるようになりました。

主要なアーキテクチャの「変更」は、本当にリファクタリングのみで行われているのでしょうか。ただし、リファクタリングのみを行っていて、テストが失敗し始めた場合は、どこかで機能を誤って変更した可能性があります。これは、ユニットテストがあなたが捕まえるのを助けることになっているものです。機能とアーキテクチャに同時に抜本的な変更を加える場合は、速度を落とし、その赤/緑/リファクタリングの溝に入ることを検討することをお勧めします。追加のテストなしで新しい(または変更された)機能はなく、リファクタリング中の機能(およびテストの中断)。

更新(コメントに基づく):

@Cybisは、リファクタリングによって動作が変更されてはならないため、リファクタリングによってテストが中断されてはならないという私の主張に対して興味深い反対意見を提起しました。彼の異議は、リファクタリングAPIを変更するため、「ブレーク」をテストすることです。

まず、リファクタリングに関する標準的なリファレンスであるMartinFowlerのblikiにアクセスすることをお勧めします。ちょうど今、私はそれをレビューしました、そして、いくつかのことが私に飛び出します:

  • インターフェイスのリファクタリングを変更していますか? Martinは、リファクタリングを「動作を維持する」変更と呼んでいます。つまり、インターフェイス/ APIが変更されると、そのインターフェイス/APIのすべての呼び出し元も変更する必要があります。テストを含めて、私は言います。
  • これは、動作が変更されたことを意味するものではありません。繰り返しになりますが、ファウラーは、リファクタリングの定義は、変更が動作 を維持することであると強調しています。

これに照らして、リファクタリング中に1つまたは複数のテストを変更する必要がある場合、これをテストの「中断」とは見なしません。これは、コードベース全体の動作を維持するためのリファクタリングの一部にすぎません。テストを変更する必要があることと、リファクタリングの一部としてコードベースの他の部分を変更する必要があることの間に違いはありません。(これは、テストをコードベースの第一級市民と見なすことについて前に言ったことに戻ります。)

さらに、リファクタリングが完了すると、テストは、変更されたテストであっても、引き続き合格することを期待しています。そのテストがテストしていたもの(おそらくそのテストのassert(s))は、リファクタリングが行われた後も有効であるはずです。それ以外の場合は、リファクタリング中に動作が何らかの形で変更/後退したことを示す危険信号です。

その主張は意味がないように聞こえるかもしれませんが、考えてみてください。本番コードベースでコードのブロックを移動し、新しいコンテキスト(新しいクラス、新しいメソッドシグネチャなど)で引き続き機能することを期待することについては何も考えていません。テストについても同じように感じます。おそらく、リファクタリングによって、テストが呼び出す必要のあるAPI、またはテストが使用する必要のあるクラスが変更されますが、最終的には、リファクタリングのためにテストのポイントが変更されることはありません。

(私が考えることができる唯一の例外は、LinkedListをArrayListなどに置き換えるなど、リファクタリング中に変更する可能性のある低レベルの実装の詳細をテストするテストです。しかし、その場合、テストは過剰にテストされており、硬すぎて壊れやすいです。)

于 2008-11-02T22:09:06.610 に答える
7

TDD がリファクタリングにもたらす主な利点は、開発者がコードを変更する勇気を持てるようになることです。単体テストの準備ができたら、開発者はあえてコードを変更して実行するだけです。xUnit バーがまだ緑色の場合は、先に進む自信があります。

個人的には TDD が好きですが、過度の TDD は推奨しません。つまり、単体テスト ケースを書きすぎないようにします。単体テストだけで十分です。単体テストをやりすぎると、アーキテクチャの変更を行うときにジレンマに陥る可能性があります。本番コードの 1 つの大きな変更により、多くの単体テスト ケースが変更されます。したがって、単体テストを十分に行ってください。

于 2008-11-01T04:04:19.483 に答える
4

TDD は、最初に失敗するテストを書くと言います。テストは、開発者がユースケース/ストーリー/シナリオ/プロセスが達成すべきことを理解していることを示すために書かれています。

次に、テストを満たすコードを記述します。

要件が変更されたり誤解されたりした場合は、最初にテストを編集または書き直してください。

レッドバー、グリーンバーですよね?

奇妙なことに、 Fowler's Refactoringはリファクタリングのリファレンスです。

Dr. Dobb のScott Ambler の一連の記事(「The Agile Edge??」) は、実際の TDD の優れたウォークスルーです。

于 2008-11-01T03:58:46.017 に答える
3

たとえば、アルゴリズムをより効率的なものに変更します。

これはリファクタリングではなく、パフォーマンスの最適化です。リファクタリングとは、既存のコードの設計を改善することです。つまり、開発者のニーズに合わせて形状を変更します。外部から見える動作に影響を与える目的でコードを変更することはリファクタリングではなく、効率化のための変更も含まれます。

TDD の価値の一部は、結果を生成する方法を変更しながら、目に見える動作を一定に保つのにテストが役立つことです。

于 2008-11-01T06:08:05.427 に答える
2

私はお勧めします(他の人が持っているように):

于 2008-11-01T04:53:59.257 に答える
1

Kent Beck の TDD の本。

最初にテストします。SOLID OOP の原則に従うことと、優れたリファクタリング ツールを使用することは、必須ではないにしても不可欠です。

于 2008-11-01T04:21:28.527 に答える
1

TDD では、どのような状況でテスト ケースを変更および削除しても問題ありませんか? テスト ケースを変更しても問題が発生しないことをどのように確認できますか? さらに、常に変化するコードと包括的なテスト スイートを同期させるのは大変なことのようです。

テストと仕様のポイントは、システムの正しい動作を定義することです。したがって、非常に簡単に:

if definition of correctness changes
  change tests/specs
end

if definition of correctness does not change
  # no need to change tests/specs
  # though you still can for other reasons if you want/need
end

したがって、アプリケーション/システムの仕様または望ましい動作が変更された場合は、テストを変更する必要があります。このような状況でコードのみを変更し、テストは変更しないというのは、明らかに方法論が壊れています。それを「面倒」と思うかもしれませんが、テスト スイートがないことはもっと苦痛です。:) 他の人が言及したように、コードを「あえて」変更する自由を持つことは、実際に非常に力を与え、解放します。:)

于 2008-11-01T04:58:15.583 に答える