TDDを行う人々は、主要なリファクタリングを行うときに仕事の損失をどのように処理しますか


37

しばらくの間、コードの単体テストを書くことを学んでいます。

最初は、真のTDDを開始しました。最初に失敗したテストを記述するまで、コードを記述しませんでした。

しかし、最近、多くのコードが関係する解決しにくい問題がありました。テストとコードの記述に数週間を費やした後、私は全体のアプローチが機能しないという不幸な結論に至り、2週間の作業を捨ててやり直す必要がありました。

これは、コードを作成したばかりの場合には十分な判断ではありませんが、数百のユニットテストを作成した場合、すべてを捨てるのはさらに感情的に困難になります。

概念実証のためにコードをまとめて、その後自分のアプローチに満足したらテストを書くことができたのに、これらのテストを書くのに3日間または4日間の努力を無駄にしたと考えるのは仕方ありません。

TDDを実践する人々は、このような状況を適切に処理しますか?いくつかのケースでルールを曲げる場合はありますか、それともコードが役に立たないことが判明した場合でも、常に最初にテストを慎重に書くのですか?


6
追加するものが何もないときではなく、奪うものが何もないとき、完璧が達成されます。-アントワーヌドサンテグジュペリ
mouviciel

12
すべてのテストが間違っている可能性はありますか?実装の変更により、作成したすべてのテストが無効になる方法を説明してください。
-S.Lott

6
@ S.Lott:テストは間違っていませんでした。関連性がなくなっただけです。素数を使用して問題の一部を解決しているとします。したがって、素数を生成するクラスを作成し、そのクラスのテストを作成して、それが機能することを確認します。今、あなたはあなたの問題に対する別の全く異なる解決策を見つけますが、それはいかなる方法でも素数を含みません。このクラスとそのテストは冗長になりました。これは、1つだけでなく10のクラスだけの私の状況でした。
-GazTheDestroyer

5
@GazTheDestroyerは、テストコードと機能コードを区別することは間違いであるように思われます。これはすべて同じ開発プロセスの一部です。TDDには通常、開発プロセスのさらに下で回復されるオーバーヘッドがあり、この場合、オーバーヘッドによって何も得られなかったように見えることに注意してください。しかし、同様に、テストは、アーキテクチャの障害についての理解をどの程度伝えましたか?そのあなたが(いや、許可されていることに注意することも重要奨励これはおそらくビット極端ではあるが...時間をかけてテストを剪定する)( - :
Murph

10
私は意味論的に慢になり、ここで@ S.Lottに同意します。あなたがやったことは、それが多くのクラスとそれらのテストを捨てる結果となる場合、リファクタリングではありません。それは再設計です。特にTDDの意味でのリファクタリングとは、テストが緑色であり、内部コードを変更し、テストを再実行しても緑色のままであることを意味します。
エリックキング

回答:


33

ここには2つの問題があると思います。1つは、元の設計が最善のアプローチではない可能性があることを事前に認識していなかったことです。事前にこれを知っていた場合は、簡単な使い捨てプロトタイプを開発し、可能な設計オプションを調査し、どちらが最も有望な方法であるかを評価することを選択した可能性があります。プロトタイピングでは、プロダクション品質のコードを記述する必要はなく、コードを磨くことではなく学習に専念するため、隅々まで(またはまったく)単体テストを行う必要はありません。

現在、量産コードの開発をすぐに開始するのではなく、プロトタイピングと実験が必要であることを理解することは、必ずしも容易ではなく、常に可能ではありません。ちょうど得た知識で武装して、あなたは次回プロトタイピングの必要性を認識することができるかもしれません。またはできない場合があります。しかし、少なくともこのオプションが考慮されることを知っています。そして、これ自体が重要な知識です。

もう一つの問題は、あなたの認識と私見です。私たちは皆、間違いを犯します。そして、振り返ってみると、私たちが何をすべきかを簡単に確認できます。これは私たちが学ぶ方法です。プロトタイプ作成が重要である可能性があることを学習する価格として、ユニットテストへの投資を書き留め、それを乗り越えます。同じ間違いを二度としないように努力してください:-)


2
私はそれが解決するのが難しい問題になること、そして私のコードがいくぶん探索的であることを知っていましたが、最近のTDDの成功から熱意に燃え上がりました。 TDDの文献は非​​常に強調しています。そう、今、私はルールが破られる可能性があることを知っています(これが私の質問が本当にすべてであったことです)私はおそらくこれを経験するためにチョークで書きます。
-GazTheDestroyer

3
「TDDの文献がすべて強調しているので、テストを続けました。」おそらくべき更新あなたのアイデアの源との質問すべてのテストが前に書かれなければならない任意のコードを。
-S.Lott

1
私はそのような考えを持っていないし、あなたがコメントからそれをどのように得たのか分かりません。
-GazTheDestroyer

1
私は答えを書くつもりでしたが、代わりにあなたのものを支持しました。はい、100万回はい:アーキテクチャの外観がまだわからない場合は、最初に使い捨てのプロトタイプを作成し、プロトタイプ作成中に単体テストを作成する必要は
ロバートハーベイ

1
@WarrenP、確かにTDDは一つの本当の道であると考える人がいます(あなたが十分に努力すれば、何でも宗教に変えることができます;-)。しかし、私は実用的であることを好みます。私にとって、TDDはツールボックスの1つのツールであり、問​​題を解決するのではなく、それが役立つ場合にのみ使用します。
ペテルトレック

8

TDDのポイントは、正確にこの問題を回避するために、小さな関数で小さな増分のコードを記述することを強制することです。1つのドメインで数週間コードを記述し、アーキテクチャを再考すると、作成したすべてのユーティリティメソッドが役に立たなくなった場合、そもそもメソッドは大きすぎるでしょう。(はい、これは今のところ正確に快適ではないことを知っています...)


3
私の方法は決して大きくはありませんでした。古いアーキテクチャとは似ていない新しいアーキテクチャを考えると、それらは単に無関係になりました。部分的には、新しいアーキテクチャがはるかに単純だったためです。
GazTheDestroyer

さて、本当に再利用可能なものが何もない場合は、損失を削減して先に進むことができます。しかし、TDDの約束は、アプリケーションコードに加えてテストコードを記述する場合でも、同じ目標をより速く達成できることです。もしそれが真実であり、私がそうだと固く思うなら、少なくともあなたは、その時間の2倍ではなく、「数週間」でアーキテクチャを行う方法に気付いた点に達しました。
キリアンフォス

1
@Kilian、「TDDの約束は、同じ目標をより早く達成することです」-ここでどの目標を参照していますか?実稼働コード自体と一緒に単体テストを記述すると、コードを単に解くのに比べて、最初は遅くなります。品質の向上とメンテナンスコストの削減により、TDDは長期的にしか返済できません。
ペテルトレック

@PéterTörök-あなたがコードを書いた時までにTDDがそれ自体にお金を払うので、TDDは決してコストがかからないと主張する人々がいます。それは確かに私には当てはまりませんが、キリアンはそれを自分で信じているようです。
psr

もし...あなたがそれを信じていないなら、実際、TDDが費用よりも相当な見返りがあると信じていないなら、それをする意味は全くありませんか?Gazが説明した特定の状況だけでなく、まったく。私は今、このスレッドを完全にトピック外に追い込んでしまったのではないかと思います:(
Kilian Foth

6

ブルックスは、「1つを捨てる計画、あなたはとにかくする」と述べた。あなたはまさにそれをしているように思えます。そうは言っても、コードの大きな帯ではなく、コードのユニットをテストするためにユニットテストを書くべきです。これらはより機能的なテストであるため、内部実装に対して行う必要があります。

たとえば、PDE(偏微分方程式)ソルバーを作成する場合、数学的に解決できることを解決しようとするいくつかのテストを作成します。これらは私の最初の「ユニット」テストです。読む:機能テストはxUnitフレームワークの一部として実行されます。これらは、PDEの解決に使用するアルゴリズムに応じて変わりません。私が気にするのは結果だけです。2番目のユニットテストは、アルゴリズムのコーディングに使用される関数に焦点を当てるので、アルゴリズム固有です(Runge-Kuttaなど)。Runge-Kuttaが不適切であることが判明した場合、それらの最上位レベルのテスト(Runge-Kuttaが適切でないことを示したテストを含む)を引き続き使用できます。したがって、2番目の反復には、最初の反復と同じテストの多くがまだあります。

あなたの問題はおそらく設計上のものであり、必ずしもコードの問題ではありません。しかし、詳細がなければ、言うことは困難です。


周辺機器だけですが、PDEとは何ですか?
CVn

1
@MichaelKjörlingそれは偏微分方程式だと思う
-foraidt

2
ブルックスは、彼の第2版でその声明を撤回しませんでしたか?
サイモン

Runge-Kuttaが適切でないことを示すテストがまだあるということですか?これらのテストはどのようなものですか?書いたRunge-Kuttaアルゴリズムが適切ではないことを発見する前に保存し、RKでのエンドツーエンドテストの実行が失敗することを意味しますか?
moteutsch 14年

5

TDDは反復プロセスであることに注意してください。小さなテストを作成し(ほとんどの場合、数行で十分です)、実行します。テストは失敗し、メインソースで直接動作し、テストに合格するようにテスト済みの機能を実装しようとします。もう一度やり直してください。

あなたが気づいたように、これはうまくいかないので、一度にすべてのテストを書くことを試みるべきではありません。これにより、使用されないテストを作成する時間を無駄にするリスクが軽減されます。


1
私は自分自身をとてもうまく説明できたとは思わない。私はテストを繰り返し書きます。それが私が突然冗長になったコードの数百のテストで終わった方法です。
GazTheDestroyer

1
上記のように-「コードのテスト」ではなく「テストコード」考えるべきだと思う
マーフ

1
+1:「すべてのテストを一度に書こうとしないでください」
-S.Lott

4

あなたはそれを自分で言ったと思う:あなたはあなたがあなたのすべてのユニットテストを書き始める前にあなたのアプローチについて確信がなかった。

私が実際に作業したTDDプロジェクト(実際にはそれほど多くない、2年間の作業をカバーするたった3つ)と理論的に学んだことを比較して学んだことは、自動テスト!排他的)。

言い換えれば、TDDのTはUを持っている必要はありません...自動化されていますが、自動化された機能テストよりも(テストクラスやメソッドのように)ユニットテストではありません:同じレベルです現在作業中のアーキテクチャとしての機能の粒度。わずかなテストと機能的な全体像でハイレベルに開始し、最終的には何千ものUTになり、すべてのクラスは美しいアーキテクチャで明確に定義されます...

ユニットテストは、チームで作業するときに、コードの変更がバグの無限のサイクルを作成するのを防ぐために非常に役立ちます。しかし、少なくともユーザーストーリーごとにグローバルに機能するPOCを作成する前に、プロジェクトの作業を開始するときにそれほど正確なことを書いたことはありません。

たぶんそれは私の個人的なやり方です。プロジェクトのパターンや構造をゼロから決定するのに十分な経験がないため、実際に最初から何百ものUTを作成するのに時間を浪費することはありません...

より一般的には、すべてを壊してすべてを投げるという考えは常にそこにあります。私たちのツールと方法でできる限り「継続的」であり、エントロピーと戦う唯一の方法は、最初からやり直すことです。しかし、目標は、それが発生したときに、実装した自動化された単体テストにより、プロジェクトが既に存在しない場合よりもコストが低くなることです。


3
よく言った-それはTDD、ないUTDDだ
スティーブンA. Loweの

素晴らしい答え。TDDの私の経験では、書面によるテストがソフトウェアの機能的な動作に焦点を当て、単体テストから離れることが重要です。クラスに必要な動作を考えることはより困難ですが、それはきれいなインターフェイスにつながり、結果の実装を潜在的に単純化します(実際に必要のない機能を追加しないでください)。
JohnTESlade

4
TDDを実践する人々は、このような状況を適切に処理しますか?
  1. プロトタイプを作成するタイミングとコーディングするタイミングを検討する
  2. ユニットテストがTDDと同じではないことを認識することにより
  3. 機能ユニットではなく、機能/ストーリーを検証するTDDテストを作成する

ユニットテストとテスト駆動開発の融合は、多くの苦悩と悲惨さの原因です。それでは、もう一度確認しましょう。

  • 単体テストは、実装における個々のモジュールと機能の検証に関係しています。UTでは、コードカバレッジメトリックや非常に高速に実行されるテストなどに重点が置かれます。
  • テスト駆動開発は、要件内の各機能/ストーリーの検証に関係しています。TDDでは、最初にテストを書く、書かれたコードが意図した範囲を超えないようにする、品質をリファクタリングするなどのことに重点が置かれます。

要約すると、ユニットテストには実装の焦点があり、TDDには要件の焦点があります。それらは同じものではありません。


「TDDには要件に焦点が当てられています」それにはまったく同意しません。TDDでの書き込みテストがあるユニットテスト。彼らは行う各関数/メソッドを確認します。TDDはありません、コードカバレッジに重点を持っていると(あなたがテストごとに30秒ほどを実行するので、彼らはもっと良いだろう)すぐに実行テスト気を。たぶん、あなたはATDDまたはBDDを考えていたのでしょうか?
-guillaume31

1
@ ian31:UTとTDDの融合の完璧な例。同意する必要があり、ソース資料en.wikipedia.org/wiki/Test-driven_developmentを参照する必要があります-テストの目的はコード要件を定義することです。BDDは素晴らしいです。ATDDについて聞いたことはありませんが、一見すると、TDD スケーリングをどのように適用するかのように見えます
スティーブンA.ロウ

TDDを完全に使用して、要件やユーザーストーリーに直接関係しない技術コードを設計できます。TDDを開始して普及させた人々を含め、Web、書籍、会議などでその数え切れないほどの例を見つけることができます。TDDはコードを記述するための技術であり、使用するコンテキストに応じてTDDであることに変わりはありません。
-guillaume31

また、あなたが述べたウィキペディアの記事から:「テスト駆動開発の高度なプラクティスは、顧客が指定した基準が受け入れテストに自動化されるATDDにつながり、それが従来のユニットテスト駆動開発(UTDD)プロセスを駆動します。 ...] ATDDを使用することで、開発チームは満足すべき特定の目標である受け入れテストを実現し、顧客がそのユーザーストーリーから本当に望むものに継続的に焦点を当てることができます。」これは、ATDDが主に要件に焦点を当てているようで、TDD(またはUTDDが言うところの)ではありません。
-guillaume31

@ ian31:「数百のユニットテストを破棄する」というOPの質問は、規模の混乱を示していました。必要に応じて、TDDを使用して小屋を構築できます。:D
スティーブンA.ロウ

3

テスト駆動開発は、開発を促進するためのものです。作成するテストは、現在作成しているコードの正確性を主張し、最初の行から開発速度を向上させるのに役立ちます。

テストは負担であり、後の段階的な開発のためだけのものであると信じているようです。この考え方はTDDに沿っていません。

静的型付けと比較することもできます:静的型情報を使用せずにコードを記述できますが、コードに静的型を追加すると、コードの特定のプロパティをアサートし、心を解放し、代わりに重要な構造に集中できるため、速度と有効性。


2

主要なリファクタリングを行う際の問題は、噛むことができる以上に噛みついたことに気付く道をたどることができることです。巨大なリファクタリングは間違いです。そもそもシステム設計に欠陥がある場合、リファクタリングを行うと、難しい決定を下す必要が生じる前の段階にとどまる可能性があります。システムをそのままにして回避するか、再設計していくつかの大きな変更を加えることを計画してください。

ただし、別の方法があります。コードをリファクタリングすることの本当の利点は、物事をより簡単に、読みやすく、さらに保守しやすくすることです。不確実な問題に取り組む場合は、変更を急ぎ、問題の詳細を知るためにどこまで導くかを調べ、急ぎを捨てて、急上昇の内容に基づいて新しいリファクタリングを適用しますあなたに教えた。問題は、手順が小さく、リファクタリングの努力が最初にテストを書く能力を超えない場合にのみ、確実にコードを確実に改善できることです。解決策は明白に思えるかもしれないので、テストを書いてからコードを書いて、さらにコードを書こうとする誘惑ですが、すぐに変更によってさらに多くのテストが変更されることに気づくので、一度に1つのものだけを変更するように注意する必要があります。

したがって、答えはリファクタリングを主要なものにしないことです。赤ちゃんのステップ。メソッドを抽出することから始めてから、重複の除去を検討します。次に、クラスの抽出に進みます。それぞれの小さなステップで、一度に1つの小さな変更が行われます。コードを抽出する場合は、最初にテストを作成します。コードを削除する場合は、そのコードを削除してテストを実行し、壊れたテストが必要になるかどうかを判断します。一度に1つの小さな赤ちゃんのステップ。時間がかかるようですが、実際にはリファクタリング時間を大幅に短縮します。

しかし現実には、すべてのスパイクは一見労力の無駄になる可能性があります。コードの変更がどこにも行かない場合があり、VCからコードを復元していることに気付きます。これは、私たちが日々行っていることの現実にすぎません。ただし、失敗したスパイクは、何かを教えてくれれば無駄になりません。失敗したすべてのリファクタリングの努力は、あなたがあまりにも早くやりすぎているか、あなたのアプローチが間違っているかもしれないことを教えてくれます。それから何かを学べば、それも時間の無駄ではありません。このようなことをすればするほど、より多くのことを学び、効率的になります。私のアドバイスは、今のところそれを着て、より少なくすることでより多くのことを学ぶことであり、これはあなたがどこにも行かない前にスパイクをどこまで取るかを特定するまで物事がおそらく必要な方法であることを受け入れることです。


1

あなたのアプローチが3日後に欠陥になった理由はわかりません。アーキテクチャの不確実性に応じて、テスト戦略の変更を検討できます。

  • パフォーマンスについて不確かな場合、パフォーマンスをアサートするいくつかの統合テストから始めることをお勧めしますか?

  • APIの複雑さが調査対象である場合は、実際の裸の小さな単体テストを作成して、それを実行する最善の方法を見つけてください。何も実装せずに、クラスでハードコードされた値を返すようにするか、NotImplementedExceptionsをスローするようにします。


0

私にとってユニットテストは、インターフェースを「実際の」使用下に置く機会でもあります(まあ、ユニットテストと同じくらいリアルです!)。

テストのセットアップを余儀なくされた場合、デザインを実行する必要があります。これは、物事を正気に保つのに役立ちます(テストが非常に複雑で、テストの作成が負担になる場合、それを使用するのはどうでしょうか?)。

これは、設計の変更を回避するのではなく、それらの必要性を明らかにします。はい、完全な書き換えは苦痛です。回避するために、私は通常(1つ以上の)プロトタイプを、おそらくPython(c ++での最終開発で)でセットアップします。

確かに、あなたはいつもこれらすべてのグッズの時間があるわけではありません。これらは正確にあなたが必要とする時期例LARGERあなたの目標を達成するための時間を...および/または制御の下ですべてのものを維持します。


0

創造的な開発者サーカスへようこそ


最初にコーディングするためのすべての「合法/合理的な」方法を尊重する代わりに、あなたにとって重要で新しいものであり、周りのサンプルがあなたの望むように見えない場合は、とりわけ直観を
試し てください:-すでに知っていることから、本能で書きます、あなたの精神と想像力ではありません。 -そしてやめて。 -虫眼鏡を使って、書いたすべての単語を調べます。「テキスト」は文字列に近いので「テキスト」と書きますが、「動詞」、「形容詞」、またはより正確なものが必要です 。 ..または、あなたは未来を考えるコードを書いたのですか?削除します -修正し、他のタスク(スポーツ、文化、またはビジネス以外のこと)を実行し、戻ってもう一度読みます。 -すべてがうまくフィットし、







-修正し、他のタスクを実行し、戻ってもう一度読みます。
-すべてうまく適合し、TDDに渡す
-これですべてが正しくなりました
。-ベンチマークを試して、最適化するものを指摘してください

表示される内容:
-すべてのルールを順守するコードを作成しました
-経験、仕事の新しい方法を取得
します-心の中で何かが変わった場合、新しい設定に恐れることはありません。

そして今、上記のようなUMLを見ると、
「上司、このためにTDDから始めます...」
と言うことができます。それは別の新しいことですか?
「上司、コーディングの方法を決める前に何か試してみます。」

敬具PARIS
Claude

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