質問
初めてのプロとしてのプロジェクトのリズムに慣れつつある初心者開発者として、私はできるだけ早く良い習慣を身につけようと努めています。ただし、テストを忘れたり、後回しにしたり、一度に 1 つずつではなく、ビルドの最後に大量のテストを実行したりすることが多いことがわかりました。
私の質問は、大規模なプロジェクトに取り組むときにどのようなリズムに乗りたいですか、そしてテストはそのリズムのどこに当てはまるかということです。
解決
TDD の人たちをフォローしたい場合は、 コードを書き始める前に ;)
私もあなたと全く同じ立場です。私はテストにもっと取り組みたいのですが、現在は「コードを正しく出力する」ことよりも「コードを出力する」ことに取り組んでいる状況なので、とても怖くなっています。そのため、私はゆっくりと開発サイクルにテストプロセスを統合しようとしています。
現在、 コードを書きながらテストし、コードを書きながらそのコードを無効にしようとします. 。TDD の考え方に入るのは難しいと思います。時間はかかりますが、それが私のやり方です 欲しい 仕事に..
編集:
おそらくこれを拡張する必要があると思いました。これが私の基本的な「作業プロセス」です...
- コード、可能なオブジェクトデザインなどから欲しいものを計画します。
- 私の最初のクラスを作成し、クラスの「ビジョン」が何であるかを概説するトップに大きなコメントを追加します。
- 基本的なテスト シナリオの概要を説明します。これらは基本的にユニットテストになります。
- 最初のメソッドを作成します。また、それがどのようであるかを説明する短いコメントを書く 期待される 働くこと。
- 自動テストを作成して、期待どおりの動作をするかどうかを確認します。
- メソッドごとに手順 4 ~ 6 を繰り返します (自動テストは F5 で実行される巨大なリストにあることに注意してください)。
- 次に、作業環境でクラスをエミュレートするための強力なテストをいくつか作成し、明らかに問題を修正します。
- これに続いて新しいバグが判明した場合は、戻って新しいテストを書き込み、テストが失敗することを確認してから (これはバグの概念実証としても機能します)、修正します。
それが役立つことを願っています..これは私の懸念事項であると述べたので、これを改善する方法についてのコメントをお待ちしています。
他のヒント
コードをチェックインする前に。
最初に、そして頻繁に。システムに新しい機能を作成する場合は、最初にインターフェイスを定義し、次にそれらのインターフェイスの単体テストを作成することになります。どのようなテストを作成するかを決めるには、インターフェースの API とそれが提供する機能を考慮し、紙とペンを取り出して、潜在的なエラー状態や、インターフェースが正しい仕事をしていることを証明する方法についてしばらく考えてください。これが難しすぎる場合は、API が十分ではない可能性があります。テストに関しては、複数の特定のオブジェクトをテストする「統合」テストの作成を避け、「単体」テストとして保持できるかどうかを確認してください。
次に、インターフェイスのデフォルトの実装 (何もせず、ゴミの値を返しますが、例外はスローしません) を作成し、それをテストにプラグインして、テストが失敗することを確認します (これにより、テストが機能するかどうかがテストされます。:))。次に、機能を記述してテストを再実行します。このメカニズムは完璧ではありませんが、多くの単純なコーディングミスをカバーし、アプリケーション全体にプラグインせずに新しい機能を実行する機会を提供します。
これに続いて、既存の機能を組み合わせてメイン アプリケーションでテストする必要があります。ここはテストがより難しい部分であり、問題を解決するコツを持っている優れた QA テスターに可能であれば部分的に委託する必要があります。これらのスキルも持っていると役立ちますが。正直に言うと、テストを正しく行うにはコツが必要です。私自身の経験は、私自身の素朴なデプロイメントと、ユーザーが怒りながら使用したときに報告されたその後のバグから来ています。
最初、これが私に起こったとき、ユーザーが意図的に私のソフトウェアを壊そうとしていることに腹立たしさを感じ、すべての「バグ」を「トレーニングの問題」としてマークしたいと思いました。しかしよく考えてみると、アプリケーションをできる限りシンプルで信頼性が高く、馬鹿でも使えるようにするのが(開発者としての)私たちの役割だと気づきました。愚か者に力を与えるのが私たちの役割であり、それが私たちがお金を受け取る理由です。バカ扱い。
このように効果的にテストするには、すべてを壊そうとする心構えを身につける必要があります。ボタンを連打し、奇妙かつ素晴らしい方法でアプリケーションを破壊しようとするユーザーのマントを想定してください。欠陥を見つけられなかった場合、本番環境で欠陥が発見され、会社にとって重大な面子が失われると想定してください。これらすべての問題に対して全責任を負い、自分が責任を負っている (あるいは一部でも責任がある) バグが運用環境で発見された場合は、自分自身を呪ってください。
上記のほとんどを実行すると、より堅牢なコードの生成が開始されるはずですが、これは少し芸術的な形式であり、上達するには多くの経験が必要です。
覚えておくと良い鍵は、
「早めにテストし、頻繁にテストし、終わったと思ったらもう一度テストしてください。」
いつテストするか?コードが正しく動作することが重要な場合。
自分で何かをハッキングするときは、最後にテストします。悪い習慣ですが、これらは通常、数回使用するだけの小さなものです。
より大きなプロジェクトでは、クラスを作成する前にテストを作成し、そのクラスに変更を加えるたびにテストを実行します。
私は常にテストを行っています。関数内のループさえ終了したら、プログラムを実行し、ループの先頭でブレークポイントにヒットし、ループを実行します。これはすべて、プロセスが意図したとおりに実行されていることを確認するためだけです。
次に、関数が完了したら、関数全体をテストします。おそらく、関数が呼び出される直前にブレークポイントを設定し、デバッガをチェックして関数が完全に動作することを確認するとよいでしょう。
私ならこう言うと思います:「頻繁にテストしてください。」
私は最近単体テストを通常のワークフローに追加したばかりですが、単体テストを作成します。
- 新しいコード モジュールごとに要件を表現するため (インターフェイスを作成した直後、実装を作成する前)
- 毎回思うのですが「もっと良かった…」終わるまでに」
- 何かが壊れたとき、バグを数値化して修正したことを証明するため
- メモリを明示的に割り当てたり割り当て解除したりするコードを書くとき、メモリ リークを探すのが大嫌いです...
私はほとんどのビルドで、常にコードを実行する前にテストを実行します。
単体テストから始めます。具体的には、TDD (テスト駆動開発) を確認してください。TDD の背後にある概念は、最初に単体テストを作成し、次にコードを作成するというものです。テストが失敗した場合は、戻ってコードをやり直します。それが通過すれば、次のステップに進みます。
私は TDD に対してハイブリッド アプローチを採用しています。私は何もないものに対してテストを書くのは好きではないので、通常は最初にコードの一部を書いてから、単体テストを入れます。これは反復的なプロセスであり、決して終わることはありません。コードを変更し、テストを実行します。失敗があれば修正して繰り返します。
もう 1 つの種類のテストは統合テストです。これはプロセスの後半で行われ、通常は QA テスト チームによって実行されます。いずれの場合でも、統合テストは、部分全体をテストする必要性に対処します。テストする必要があるのは、実際に動作する製品です。これは、通常、自動テスト ツール (ロボットなど) を使用する必要があるため、対処がより困難です。
また、継続的なビルドを実行するには、CruiseControl.NET などの製品も検討してください。CC.NET は、ビルドごとに単体テストを実行し、失敗があればすぐに通知してくれるので便利です。
ここでは TDD は行いません (ただし、TDD を提唱する人もいます)。しかし、私たちのルールでは、変更を加えた単体テストをチェックインする必要があります。常に起こるわけではありませんが、特定の変更セットに戻ってテストが記述されているかどうかを確認するのは簡単です。
新しい機能の作成が終わるまでテストを待っていると、その機能が壊れる可能性があると考えたエッジ ケースの多くを忘れてしまうことがわかりました。自分自身で学ぶために何かをしているのであればこれは問題ありませんが、プロフェッショナルな環境では、私のフローは次のような典型的な形式であることがわかります。赤、緑、リファクタリング。
赤:テストが失敗するように作成します。こうすることで、テストが正しい変数に対してアサートしていることがわかります。
緑:可能な限り簡単な方法で新しいテストに合格します。それがハードコーディングすることを意味する場合でも、問題ありません。これは、すぐに何かを実行したい人にとっては最適です。
リファクタリング:テストに合格したので、自信を持って戻ってコードを変更できます。新しい変更によりテストが中断されましたか?素晴らしいですね、あなたの変更にはあなたが気づかなかった影響があったことが、テストでわかりました。
このリズムにより、私は時間の経過とともに開発をスピードアップすることができました。なぜなら、私は基本的に、機能を動作させるためにチェックする必要があると考えたすべての項目の履歴コンパイラーを持っているからです。これにより、他にも多くの利点が得られますが、ここでは説明しません...
ここには素晴らしい答えがたくさんあります!
意味のある最低レベルでテストしようとします。
1 つの計算または条件が困難または複雑な場合は、作成中にテスト コードを追加し、各部分が機能することを確認します。完了したらテスト コードをコメント アウトしますが、アルゴリズムをどのようにテストしたかを文書化するためにそのままにしておきます。
各機能をテストします。
- 各ブランチを少なくとも 1 回実行します。
- を練習します。 境界条件 -- コードの動作が変更される入力値 -- 「1 ずつずれた」エラーを検出します。
- 有効な入力と無効な入力のさまざまな組み合わせをテストします。
- コードが壊れる可能性のある状況を探してテストします。
上記と同じ戦略で各モジュールをテストします。
コード本体を全体としてテストして、コンポーネントが適切に相互作用することを確認します。下位レベルのテストに熱心に取り組んでいる場合、これは本質的に、組み立て中に何も壊れていないことを確認するための「信頼性テスト」です。
私のコードのほとんどは組み込みデバイス用であるため、堅牢性、さまざまなスレッド、タスク、コンポーネント間の相互作用、予期しないリソースの使用に特に注意を払っています。メモリ、CPU、ファイルシステムスペース、 等.
一般に、エラーに遭遇するのが早ければ早いほど、それを切り分け、特定し、修正することが容易になります。また、後を追うのではなく、作成に多くの時間を費やすことができます。*
**わかっています、-1 は無償のバッファ ポインタ参照です!*