23

私が行ったコードのほとんどについて単体テストを作成しましたが、ケント・ベックの例でTDDのコピーを入手したのはつい最近のことです。アプリケーションを「テスト可能」にすることができなかったため、私が行った特定の設計上の決定を常に後悔しています。私はこの本を読みましたが、一部は異質に見えますが、それを管理できると感じ、基本的に2つの部分が通信するクライアント/サーバーシステムである現在のプロジェクトで試してみることにしました。USB。1つはガジェットに、もう1つはホストにあります。アプリケーションはPythonです。

私は始めてすぐに、書き直しの混乱と小さなテストに巻き込まれましたが、後で実際には何もテストされていないと思いました。私はそれらのほとんどを捨てました、そして今、テストがすべてたった2つに凝固した実用的なアプリケーションを持っています。

私の経験に基づいて、私が聞きたいいくつかの質問があります。NewからTDDまでいくつかの情報を入手しました:TDDの実行方法を示すテスト付きのサンプルアプリケーションはありますか?しかし、私が答え/議論したいいくつかの特定の質問があります。

  1. Kent Beckは、開発プロセスをガイドするために、彼が追加および削除したリストを使用します。どのようにしてそのようなリストを作成しますか?最初は「サーバーを起動する必要がある」、「チャネルが利用できない場合はサーバーを中止する必要がある」などの項目がいくつかありましたが、それらは混ざり合い、最終的には「クライアントがサーバーに接続できる必要があります」のようなものになりました。包含サーバーの起動など)。
  2. 書き換えはどのように処理しますか?最初は名前付きパイプに基づく半二重システムを選択したので、自分のマシンでアプリケーションロジックを開発し、後でUSB通信部分を追加できました。それらはソケットベースのものに移行し、次にrawソケットの使用からPythonSocketServerモジュールの使用に移行しました。状況が変わるたびに、面倒なテストのかなりの部分を書き直さなければならないことに気づきました。私は、テストが私の開発中にいくぶん不変のガイドになるだろうと考えました。彼らは、処理するコードが増えたように感じました。
  3. どちらかの側をテストするために、チャネルを介して通信するためのクライアントとサーバーが必要でした。片方をモックしてもう片方をテストすることはできましたが、チャンネル全体がテストされなかったので、それを見逃してしまうのではないかと心配しています。これは、赤/緑/リファクタリングのリズム全体を損ないました。これは単に経験不足ですか、それとも私は何か間違ったことをしていますか?
  4. 「あなたがそれを作るまでそれを偽造する」は私にたくさんの厄介なコードを残しました、そしてそれは後で私がリファクタリングとクリーンアップするために多くの時間を費やしました。これは物事が機能する方法ですか?
  5. セッションの最後に、クライアントとサーバーを約3〜4個の単体テストで実行しています。それをするのに約1週間かかりました。コードの後に​​単体テストを使用していれば、1日でそれを実行できたと思います。ゲインがわかりません。

この方法論を使用して、大規模で重要なプロジェクトを完全に(またはほぼ完全に)実装した人々からのコメントとアドバイスを探しています。すでに何かを実行していて、新しい機能を追加したい場合は、その方法に従うのが理にかなっていますが、最初からそれを行うのは面倒で、努力する価値がないようです

PS:これがコミュニティウィキであるかどうかを教えてください。そのようにマークします。

更新0:すべての回答が等しく役に立ちました。自分の経験に最も共鳴したので、自分がやったものを選びました。

アップデート1:練習練習練習!

4

7 に答える 7

10

予備的なコメントとして、TDDは練習をします。TDDを始めたときに書いたテストを振り返ると、数年前に書いたコードを見るときと同じように、多くの問題があります。それを続けてください、そしてあなたが悪いものから良いコードを認識し始めるのと同じように、同じことがあなたのテストでも起こります-忍耐をもって。

どのようにしてそのようなリストを作成しますか?最初は「サーバーを起動する必要がある」、「チャネルが利用できない場合はサーバーを中止する必要がある」などの項目がいくつかありましたが、それらが混ざり合い、最終的には「クライアントがサーバーに接続できる必要があります」のようなものになりました。

「リスト」はかなり非公式かもしれませんが(ベックの本の場合です)、アイテムをテストに移すときは、「[これに何かが起こったとき]、[この条件はその]"形式。これにより、検証対象、検証方法、およびテストに直接変換する方法について、より深く考える必要があります。そうでない場合は、どの機能が欠落しているかについての手がかりが得られるはずです。ユースケース/シナリオを考えてください。たとえば、「サーバーを起動する必要がある」というのは、誰もアクションを開始していないため、不明確です。

状況が変わるたびに、面倒なテストのかなりの部分を書き直さなければならないことに気づきました。私は、テストが私の開発中にいくぶん不変のガイドになるだろうと考えました。彼らは、処理するコードが増えたように感じました。

まず、はい、テストはより多くのコードであり、メンテナンスが必要です-そしてメンテナンス可能なテストを書くには練習が必要です。私はS.Lottに同意します。テストを大幅に変更する必要がある場合は、おそらく「深すぎる」​​テストを行っていることになります。理想的には、変更される可能性が低いパブリックインターフェイスのレベルでテストし、進化する可能性のある実装の詳細のレベルではテストしないようにします。ただし、演​​習の一部は設計の作成に関するものであるため、一部が間違っていることを予期し、テストも移動/リファクタリングする必要があります。

片方をモックしてもう片方をテストすることはできましたが、チャンネル全体がテストされなかったので、それを見逃してしまうのではないかと心配しています。

それについては完全にはわかりません。その音から、モックを使用することは正しい考えでした。一方を取り、もう一方をモックし、もう一方が適切に実装されていると仮定して、それぞれが機能することを確認します。システム全体を一緒にテストすることは統合テストです。これも実行したいのですが、通常はTDDプロセスの一部ではありません。

「あなたがそれを作るまでそれを偽造する」は私にたくさんの厄介なコードを残しました、そしてそれは後で私がリファクタリングとクリーンアップするために多くの時間を費やしました。これは物事が機能する方法ですか?

TDDを実行している間は、リファクタリングに多くの時間を費やす必要があります。一方、偽造する場合、それは一時的なものであり、次のステップは偽造を解除することです。通常、偽造したために複数のテストに合格する必要はありません。一度に1つのピースに焦点を合わせ、できるだけ早くリファクタリングする必要があります。

コードの後に​​単体テストを使用していれば、1日でそれを実行できたと思います。ゲインがわかりません。

繰り返しになりますが、練習が必要であり、時間の経過とともに速くなるはずです。また、TDDが他のTDDよりも実り多い場合もあります。状況によっては、書きたいコードが正確にわかっている場合は、コードの大部分を記述してからテストを記述した方が速い場合があります。
ベックの他に、私が楽しんだ本の1つは、ロイ・オシェロフによる「ユニットテストの芸術」です。これはTDDの本ではなく、.Net指向ですが、とにかく見てみたいと思うかもしれません。重要なのは、保守可能なテストの作成方法、テストの品質、および関連する質問です。私は、この本がテストを書いた後の私の経験に共鳴し、時々それを正しく行うのに苦労したことを発見しました...
だから私のアドバイスは、タオルをあまり速く投げないで、しばらく待ってください。また、もっと簡単なことを試してみることもできます。サーバー通信に関連するものをテストすることは、最初から最も簡単なプロジェクトのようには思えません。

于 2010-01-17T05:01:14.460 に答える
8
  1. Kent Beckはリストを使用しています...ついに、これは「クライアントがサーバーに接続できるはずです」(サーバーの起動などを含む)のようなものです。

多くの場合、悪い習慣です。

アーキテクチャの個別のレイヤーごとに個別のテストを行うことをお勧めします。

統合されたテストは、アーキテクチャの問題を曖昧にする傾向があります。

ただし、パブリック関数のみをテストしてください。すべての機能ではありません。

また、テストの最適化に多くの時間を費やさないでください。テストの冗長性は、動作中のアプリケーションの場合ほど害はありません。状況が変化し、1つのテストが機能したが、別のテストが失敗した場合は、おそらくテストをリファクタリングできます。以前ではありません。

2.書き換えをどのように処理しますか?...テストのかなりの部分を書き直さなければならないことがわかりました。

あまりにも低いレベルの詳細でテストしています。最も外側のパブリックな可視インターフェースをテストします。変わらないはずの部分。

はい、アーキテクチャの大幅な変更は、テストの大幅な変更を意味します。

テストコードは、物事が機能することを証明する方法です。これは、アプリケーション自体とほぼ同じくらい重要です。はい、それはより多くのコードです。はい、あなたはそれを管理しなければなりません。

3.どちらかの側をテストするために、チャネルを介して通信するためのクライアントとサーバーが必要でした。片方をモックしてもう片方をテストすることはできましたが、チャンネル全体がテストされませんでした...

ユニットテストがあります。モック付き。

全体をテストする統合テストがあります。

それらを混同しないでください。

単体テストツールを使用して統合テストを実行できますが、それらは別のものです。

そして、あなたは両方をする必要があります。

4.「あなたがそれを作るまでそれを偽造する」は、私が後でリファクタリングとクリーンアップに多くの時間を費やした多くの厄介なコードを私に残しました。これは物事が機能する方法ですか?

はい。それはまさにそれがどのように機能するかです。長い目で見れば、すべての設計を前もってやろうとして頭を悩ませるよりも、これが効果的だと感じる人もいます。一部の人々はこれが好きではなく、すべての設計を前もってやりたいと思っています。必要に応じて、事前に多くの設計を自由に行うことができます。

リファクタリングは良いことであり、事前の設計は難しすぎることがわかりました。たぶんそれは私が40年近くコーディングしていて、私の脳がすり減っているからでしょう。

5.ゲインがわかりません。

すべての真の天才は、テストが彼らを遅くすることに気づきます。

残りの人は、コードが機能することを証明する一連のテストが完了するまで、コードが機能するかどうかを確認できません。

コードが機能することを証明する必要がない場合は、テストする必要はありません。

于 2010-01-14T18:55:00.073 に答える
3

Q. Kent Beckは、開発プロセスをガイドするために、彼が追加および削除したリストを使用しています。どのようにしてそのようなリストを作成しますか?最初は「サーバーを起動する必要がある」、「チャネルが利用できない場合はサーバーを中止する必要がある」などの項目がいくつかありましたが、それらは混ざり合い、最終的には「クライアントがサーバーに接続できる必要があります」のようなものになりました。包含サーバーの起動など)。

私はチェックするかもしれないものを選ぶことから始めます。あなたの例では、「サーバーの起動」を選択しました。

サーバーが起動します

今、私は自分が書きたいと思うかもしれないもっと簡単なテストを探しています。バリエーションが少なく、可動部品が少ないもの。たとえば、「正しく構成されたサーバー」を検討するかもしれません。

サーバーが正しく構成されている
サーバーが起動します

ただし、実際には、「サーバーの起動」は「正しく構成されたサーバー」に依存するため、そのリンクを明確にします。

サーバーが正しく構成されている
正しく構成されている場合、サーバーが起動します

今、私はバリエーションを探します。私は「何がうまくいかないのか」と尋ねます。サーバーを正しく構成できませんでした。重要な方法はいくつありますか?それらのそれぞれがテストを行います。サーバーを正しく構成したのに、サーバーが起動しない場合はどうすればよいですか?そのそれぞれのケースがテストを行います。

Q.書き換えはどのように処理しますか?最初は名前付きパイプに基づく半二重システムを選択したので、自分のマシンでアプリケーションロジックを開発し、後でUSB通信部分を追加できました。それらはソケットベースのものに移行し、次にrawソケットの使用からPythonSocketServerモジュールの使用に移行しました。状況が変わるたびに、面倒なテストのかなりの部分を書き直さなければならないことに気づきました。私は、テストが私の開発中にいくぶん不変のガイドになるだろうと考えました。彼らは、処理するコードが増えたように感じました。

私が振る舞いを変えるとき、私はテストを変えること、そして最初にそれらを変えることさえ合理的であると思います!ただし、変更中の動作を直接チェックしないテストを変更する必要がある場合は、テストが非常に多くの異なる動作に依存していることを示しています。これらは統合テストであり、詐欺だと思います。(Google「統合テストは詐欺です」)

Q.どちらかの側をテストするために、チャネルを介して通信するためのクライアントとサーバーが必要でした。片方をモックしてもう片方をテストすることはできましたが、チャンネル全体がテストされなかったので、それを見逃してしまうのではないかと心配しています。これは、赤/緑/リファクタリングのリズム全体を損ないました。これは単に経験不足ですか、それとも私は何か間違ったことをしていますか?

クライアント、サーバー、チャネルを構築する場合は、それぞれを個別にチェックしようとします。私はクライアントから始めて、それを試乗するときに、サーバーとチャネルがどのように動作する必要があるかを決定します。次に、必要な動作に一致するようにチャネルとサーバーをそれぞれ実装します。クライアントをチェックするとき、私はチャネルをスタブします。サーバーをチェックするとき、私はチャネルをモックします。チャネルをチェックするとき、私はクライアントとサーバーの両方をスタブしてモックします。このクライアント、サーバー、およびチャネルの性質についていくつかの深刻な仮定を立てる必要があるため、これがあなたにとって理にかなっていることを願っています。

Q.「あなたがそれを作るまでそれを偽造する」は、私が後でリファクタリングとクリーンアップに多くの時間を費やした多くの厄介なコードを私に残しました。これは物事が機能する方法ですか?

クリーンアップする前に「偽の」コードを非常に乱雑にすると、偽造に時間がかかりすぎた可能性があります。そうは言っても、TDDを使用してより多くのコードをクリーンアップすることになったとしても、全体的なリズムははるかに良く感じられます。これは練習から来ています。

Q.セッションの終わりに、クライアントとサーバーを約3または4の単体テストで実行しています。それをするのに約1週間かかりました。コードの後に​​単体テストを使用していれば、1日でそれを実行できたと思います。ゲインがわかりません。

クライアントとサーバーが非常に単純でない限り、それらを徹底的にチェックするには、それぞれ3つまたは4つ以上のテストが必要であると言わざるを得ません。私はあなたのテストが一度に多くの異なる振る舞いをチェックする(または少なくとも実行する)と推測します、そしてそれはあなたがそれらを書くのにかかった努力を説明するかもしれません。

また、学習曲線を測定しないでください。私の最初の実際のTDD体験は、9、14時間の日数で3か月分の作業を書き直すことでした。実行に12分かかった125のテストがありました。自分が何をしているのかわからず、ゆっくりと感じましたが、安定していて、素晴らしい結果が得られました。私は基本的に3週間で書き直しましたが、最初は間違いを犯すのに3か月かかりました。今書いたら、たぶん3〜5日でできると思います。違い?私のテストスイートには、実行に1〜2秒かかる500のテストがあります。それは練習に伴いました。

于 2010-01-20T08:10:51.827 に答える
2

初心者プログラマーとして、テスト駆動開発について私がトリッキーだと思ったのは、テストを最初に行うべきだという考えでした。

初心者にとって、それは実際には真実ではありません。デザインが最初です。(インターフェイス、オブジェクトとクラス、メソッド、言語に適したものは何でも。)次に、それにテストを記述します。次に、実際に処理を実行するコードを記述します。

この本を見てから久しぶりですが、ベックはコードのデザインが無意識のうちに頭の中で起こっているように書いているようです。経験豊富なプログラマーにとってはそうかもしれませんが、私のような初心者にとってはそうですね。

コードコンプリートの最初の数章は、デザインについて考えるのに非常に役立ちます。彼らは、実装の本質的なレベルに落ち込んだとしても、設計が変更される可能性があるという事実を強調しています。その場合、テストは設計と同じ仮定に基づいているため、テストを書き直す必要があるかもしれません。

コーディングは難しいです。買い物に行きましょう。

于 2010-02-10T15:23:36.187 に答える
1

ポイント1については、最初のポイントに関して私がしばらく前に尋ねた質問を参照してください。

他のポイントを順番に処理するのではなく、グローバルなアドバイスを提供します。練習。実際にTDDを取得するには、かなりの時間がかかり、いくつかの「危険な」プロジェクト(個人的なものですが)が必要でした。TDDが非常に優れている理由について、はるかに説得力のある理由から、Googleだけです。

私のコードのデザインを推進するテストにもかかわらず、私はまだホワイトボードを手に入れ、いくつかのデザインを書き留めています。このことから、少なくともあなたは自分が何をしようとしているのかについてある程度の考えを持っています。次に、必要だと思うフィクスチャごとのテストのリストを作成します。作業を開始すると、さらに多くの機能とテストがリストに追加されます。

あなたの質問から際立っていたのは、テストを書き直すという行為です。これは、状態ではなく、行動テストを実行しているように聞こえます。言い換えれば、テストはコードに密接に関連しているように聞こえます。したがって、出力に影響を与えない単純な変更は、いくつかのテストを中断します。ユニットテスト(少なくとも優れたユニットテスト)も習得するスキルです。

Google Testing Blogの記事のいくつかは、TDDプロジェクトのテストをはるかに改善したので、私はGoogleTestingBlogを非常に強くお勧めします。

于 2010-01-14T18:42:42.063 に答える
1

名前付きパイプは正しいインターフェイスの背後に配置され、そのインターフェイスの実装方法を変更すると(名前付きパイプからソケット、別のソケットライブラリに)、そのインターフェイスを実装するコンポーネントのテストにのみ影響するはずです。だから、物事をもっと/違った形で切り詰めることは助けになったでしょう...ソケットが後ろにあるそのインターフェースはおそらく進化するでしょう。

私はおそらく6ヶ月前にTDDを始めましたか?私はまだ自分自身を学んでいます。時間の経過とともに、テス​​トとコードは大幅に改善されたと言えるので、それを維持してください。XUnitDesignPatternsという本もお勧めします。

于 2010-01-14T18:55:09.603 に答える
1

開発プロセスをガイドするために、このようなリストをどのように作成して追加し、削除しますか?私は当初、「サーバーを起動する必要がある」、「チャネルが利用できない場合はサーバーを中止する必要がある」などの項目をいくつか持っていました

TDD TODOリストの項目は、それよりもきめ細かく、たとえば次のように、1つのメソッドの1つの動作のみをテストすることを目的としています。

  • 成功したクライアント接続をテストする
  • クライアント接続エラータイプ1をテストします
  • クライアント接続エラータイプ2をテストします
  • 成功したクライアント通信をテストする
  • 接続されていない場合、テストクライアントの通信は失敗します

与えたすべての例について、テスト(ポジティブおよびネガティブ)のリストを作成できます。さらに、単体テストでは、サーバーとクライアントの間に接続を確立しません。メソッドを単独で呼び出すだけです...これは質問3に答えます。

書き換えはどのように処理しますか?

単体テストが実装ではなく動作をテストする場合、それらを書き直す必要はありません。単体テストコードが実際に本番コードと通信するための名前付きパイプを作成する場合、パイプからソケットに切り替えるときに明らかにテストを変更する必要があります。単体テストは、ファイルシステム、ネットワーク、データベースなどの外部リソースから遠ざける必要があります。これらは低速であり、使用できない可能性があるためです...これらの単体テストルールを参照してください。

これは、最低レベルの機能が単体テストされていないことを意味します。システム全体がエンドツーエンドでテストされる統合テストでテストされます。

于 2010-01-15T17:57:23.343 に答える