目的がわからないコードのテストを書く


59

私は最近、ブラックボックスのリファクタリングを完了しました。テストする方法がわからないので、チェックインできません。

高レベルでは、クラスBから値を取得する初期化を含むクラスがあります。クラスBが「空」の場合、適切なデフォルトが生成されます。クラスBを同じデフォルトに初期化するメソッドにこの部分を抽出しました。

どちらのクラスの目的/コンテキスト、またはそれらがどのように使用されるかについてはまだ解明していません。そのため、空のクラスBからオブジェクトを初期化することはできませんし、正しい値を持っている/正しいことを行っていることを確認してください。

私の最善のアイデアは、元のコードを実行し、初期化されたメンバーに応じてパブリックメソッドの結果をハードコードし、それに対して新しいコードをテストすることです。この考えに漠然と不快感を覚える理由を明確に述べることはできません。

ここにはより良い攻撃がありますか?


28
あなたが間違った目的で始めたように感じます。最初にコードを理解してからテストし、次にリファクタリングする必要があります。コードの目的を知らずにリファクタリングするのはなぜですか?
ジェイコブライレ

11
@JacobRaihleこれは、私が今まで触れたことのない程度の学位を持つ人々のためのかなり専門的なプログラムです。進行中にコンテキストを取り上げていますが、開始する前にしっかりとした理解を得るのを待つのは実際的ではありません。
JETM

4
実用的ではないのは、物事を書き直し、変更が本番環境にあるときに、必要のない理由を発見することです。それまでに徹底的にテストできるなら、これはコードベースを知る良い方法です。そうでない場合は、変更する前に理解することが不可欠です。
ジェイコブライレ

37
システムの実際の動作をテストする場合、特性テストと呼ばれる特定の種類のテストがあります。元のシステムを取得し、それが実際に行うことをアサートするテストを追加します(必ずしも意図したものではありません!)。これらはシステムの足場として機能しますが、動作を確実に維持できるため、安全に変更できます。
ビンセントサバード

3
あなたが求めることができない/それが誰かによってレビューを取得、それを理解できますか?
pjc50

回答:


122

元気です!

多くの場合、コンポーネントをリファクタリング可能にするには、自動化された回帰テストを作成するのが最善の方法です。驚くかもしれませんが、入力と出力の「インターフェース」(その言葉の一般的な意味)を理解している限り、コンポーネントが内部で何をするのかを完全に理解せずにそのようなテストを書くことができます。これは、クラスだけでなく、本格的なレガシーアプリケーションに対しても過去に数回行いました。これは、十分に理解していないことを壊さないようにするのに役立ちました。

ただし、十分なテストデータを用意し、そのコンポーネントのユーザーの観点からソフトウェアの機能をしっかりと理解しておく必要があります。そうしないと、重要なテストケースを省略するリスクがあります。

リファクタリングを開始する前に自動テストを実装することをお勧めします。その後ではなく、小さなステップでリファクタリングを行い、各ステップを検証できます。リファクタリング自体がコードを読みやすくする必要があるので、少しずつ内部の理解を深めるのに役立ちます。したがって、このプロセスの注文手順は

  1. 「外部から」コードを理解する、
  2. 回帰テストを書く、
  3. リファクタリングは、コードの内部のより良い理解につながります

21
「レガシーコードの使用」という本にも正確に記載されている完璧な答え
Altoyr

私は一度このようなことをしなければなりませんでした。変更する前にアプリケーションから一般的な出力データを収集し、同じテストデータを実行してアプリケーションの新しいバージョンを確認します。30年前... Fortran ...なんらかの画像処理/マッピングの問題だったので、出力を見たりテストケースを書いたりしても、出力が「どうあるべきか」を本当に知りませんでした。そして、私はテクトロニクスのベクター(永続的)ディスプレイでそれを行いました。政府の仕事... 2テレタイプが私の後ろにぶつかります。

4
追加するかもしれませんが、事後も古いコードのテストを書くことができます。次に、リファクタリングしたバージョンでそれらを試すことができます。それが壊れている場合は、コミット履歴を二分検索して、壊れ始めるポイントを見つけます。
CodeMonkey

2
もう1つやることをお勧めします。テストデータの収集中に、可能であればコードカバレッジ統計を収集します。テストデータが問題のコードをどの程度正確に記述しているかがわかります。
リオリ

2
@nocomprende、先週、レガシーの科学的Fortran 77コードでその正確なことをしたのは面白いです。ASCIIデータの印刷をファイルに追加し、入力と予想される出力を使用してテストディレクトリを設定しました。私のテストケースは、2つの出力セットの差分でした。文字と文字が一致しない場合、何かを壊しました。コードの大部分が2〜3k LoCの2つのサブルーチンである場合、どこかから開始する必要があります。
ゴドリックシーア

1

単体テストを記述する重要な理由は、コンポーネントAPIを何らかの形で文書化することです。ここでは、テスト中のコードの目的を理解していないことが本当に問題です。コードカバレッジはもう1つの重要な目標であり、どの実行ブランチが存在し、どのようにトリガーされるかを知らずに達成することは困難です。

ただし、状態をきれいにリセットする(または新しいテストオブジェクトを毎回作成する)ことが可能な場合は、システムにほとんどランダムな入力を送り、出力を観察する「ゴミ箱からゴミ箱へ」タイプのテストを書くことができます。

そのようなテストは、失敗した場合に、その理由と深刻さを言うのが複雑なため、維持するのが困難です。報道は疑わしいかもしれません。しかし、彼らはまだ何もないよりもはるかに優れています。そのようなテストが失敗すると、開発者は最新の変更をより注意して修正し、できればバグを見つけることができます。


1
どのような種類の情報も、盲目の飛行よりも優れています。以前は、クラッシュダンプファイル(Unix)でデバッガーを呼び出し、スタックトレースを要求することで、運用環境にあったサーバープログラムのバグを見つけました。障害が発生した関数の名前を教えてくれました。他の知識がなくても(このデバッガの使用方法がわかりませんでした)、それがなければ神秘的で再現性のない状況になりました。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.