独自のコードのテストを改善する方法


45

私は比較的新しいソフトウェア開発者であり、改善すべきと思うことの1つは、自分のコードをテストする能力です。新しい機能を開発するたびに、バグを見つけるために、考えられるすべてのパスをたどることは非常に困難です。私はすべてがうまくいく道をたどる傾向があります。これはプログラマーがよく知っている問題であることは知っていますが、現在の雇用主にはテスターがいないので、同僚はこれにかなり優れているようです。

私の組織では、テスト駆動開発もユニットテストも行っていません。それは私を大いに助けますが、これが変わる可能性は低いです。

これを克服するために私にできることは何ですか?独自のコードをテストするとき、どのアプローチを使用しますか?


28
組織がTDDまたはユニットテストを使用していないからといって、納期を守り、品質の高いコードを作成し続ける限り、使用できないというわけではありません。
トーマスオーエンズ

1
トーマスが私にbeatられたと思いますが、私は同じような状況にいます。私は、コードの変更が何をすべきかについて非常に高いレベルの期待を書き、できればいつできるか単体テストを書きます(会社が正式に単体テストを行っていない場合でも)。それらをコミットする必要はありません。それらは、関数がどのように動作する(または修正した後に動作する)かを学習するための優れた方法です。
ブライアン

6
@ブライアン、他の人が現在それらを使用しているかどうかに関係なくコミットする必要があると思います。おそらく、優れた実践を示すことで、他の人が従うようになるでしょう。
CaffGeek

回答:


20

コーダーの仕事は物を作ることです。

テスターの仕事は物事を壊すことです。

最も難しいのは、今構築したものを壊すことです。この心理的障壁を乗り越えることによってのみ成功します。


27
-1コーダーの仕事は、機能するものを構築することです。それには常にある程度のテストが必要です。別のテスターの役割が必要であることに同意しますが、それが唯一の防衛線ではありません。
マシューロダトゥス

8
@Matthew Rodatus-コーダー側で行われるテストの量は、実際に機能するものが実際に機能することを確認することのみを目的としています。テスター側の目的は、コードが機能することを観察することではなく、バグを見つけることです。
ムービシエル

2
それはあなたの答えとは異なります。私はそれ以上に同意します。しかし、私はまだ完全に同意しません。高品質のコードを書くことは、失敗の可能性を事前に考えることを学ぶにつれて実践を通してもたらされます。失敗の可能性を創造することを学ばずに、失敗の可能性を考え抜くことはできません。私はコーダーが唯一の防衛線であるべきだとは思いませんが、彼らは最初の防衛線であるべきです。危機にTheしている問題は、技術の徹底と習熟の1つです。
マシューロダトゥス

2
@mouviciel-誤った二分法。コーダーの仕事は機能するものを構築することであり、彼は自分のコードが機能するはずの条件の下でアプリオリを考えることでそれを行います。そして、これは、少なくとも、破壊テスト+アドホック境界分析(少なくとも、もう一度)作成することによって検証されます。また、仕様に対して優れたコーダーが機能し、仕様(有効な場合)は常にテスト可能です。(。。あなたは、通常のreqを書き込むことによってそれを行うあなたがテストに合格するコードを持ってまで、最初は失敗のテストを)良いコーダは、これらの要件は、コードで満たされていることを確認することをテスト開発して
luis.espinal

2
@Dave Lasley-これはまさに私のポイントです。建築家は自分の家を倒すのに最適な人ではありません。彼はその欠陥を見ることができることの強さを誇りに思っています。別の建築家(通りの外の人ではない)だけが家に客観的な目を持ち、前の建築家が想像できないほどの特定の条件下で家が壊れる可能性があることを見つけることができます。
ムービシエル

14

Franciso、あなたが言ったことに基づいて、ここでいくつかの仮定をします。

「TDDもユニットテストも行いません。それは大いに役立ちますが、これが変わる可能性は低いです。」

このことから、あなたのチームはテストにあまり価値を置いていないか、管理者がチームが既存のコードを試して技術的な負債を最小限に抑えるための時間を費やしていないと思います。

まず、チーム/管理者にテストの価値を納得させる必要があります。外交する。経営陣がチームの前進を続けている場合は、リリースごとの欠陥率など、いくつかの事実を示す必要があります。欠陥の修正に費やした時間は、アプリケーションの改善や将来の要件への適応性の向上など、他のことによりよく費やすことができます。

チームと経営陣が一般的にコードの修正に無関心であり、コードに不満を感じている場合は、私が言ったように彼らを納得させない限り、別の職場を探す必要があるかもしれません。私が働いたすべての場所で、この問題にさまざまな程度で遭遇しました。適切なドメインモデルの欠如から、チーム内のコミュニケーション不足まで、あらゆるものが考えられます。

あなたのコードと開発する製品の品質を気にすることは良い属性であり、常に他の人に奨励したいものです。


11

C、Objective-C、またはC ++でコーディングする場合、CLang Static Analyzerを使用して、実際に実行せずにソースを批判できます。

使用可能なメモリデバッグツールがいくつかあります。ValGrind、Mac OS XのGuard Malloc、* NIXのElectric Fenceです。

一部の開発環境には、新しく割り当てられたページや新しく解放されたページをゴミで埋めたり、割り当てられていないポインターの解放を検出したり、各ヒープブロックの前後にデータを書き込んだりするデバッガーアロケーターを使用するオプションがありますそのデータの既知のパターンが変更された場合に呼び出されます。

Slashdotのある人は、デバッガーの新しいソースラインをシングルステップ実行することで多くの価値を得たと言いました。「それだ」と彼は言った。私は彼のアドバイスにいつも従うわけではありませんが、私が持っているとき、それは私にとって非常に役に立ちました。珍しいコードパスを刺激するテストケースがない場合でも、デバッガーで変数をいじってそのようなパスを取得できます。たとえば、メモリを割り当ててから、デバッガーを使用して新しいポインターをNULLに設定します。メモリアドレス、次に割り当て失敗ハンドラーをステップ実行します。

アサーション-C、C ++、およびObjective-Cのassert()マクロを使用します。あなたの言語がアサート関数を提供していない場合は、自分でアサート関数を書いてください。

アサーションを自由に使用し、コードに残します。私はassert()を「テストを続けるテスト」と呼びます。私はほとんどの関数のエントリポイントで前提条件を確認するために最も一般的に使用します。これは、Eiffelプログラミング言語に組み込まれている「Programming by Contract」の一部です。もう1つの部分は事後条件です。つまり、関数の戻り点でassert()を使用しますが、前提条件ほど多くのマイレージを得ることはできません。

assertを使用して、クラスの不変条件を確認することもできます。クラスには不変条件がまったく必要ではありませんが、最も賢明に設計されたクラスには不変条件があります。クラス不変条件とは、オブジェクトを一時的に矛盾した状態にする可能性のあるメンバー関数の内部を除き、常に真である条件です。そのような関数は、戻る前に常に一貫性を復元する必要があります。

したがって、すべてのメンバー関数は、入り口と出口で不変式をチェックでき、クラスは、他のコードがいつでも呼び出すことができるCheckInvariantという関数を定義できます。

コードカバレッジツールを使用して、ソースのどの行が実際にテストされているかを確認し、テストされていない行を刺激するテストを設計します。たとえば、物理メモリがほとんどなく、スワップファイルがないか非常に小さいVMで構成されたVM内でアプリを実行することで、低メモリハンドラを確認できます。

(何らかの理由でBeOSはスワップファイルなしで実行できましたが、そのように非常に不安定でした。BFSファイルシステムを作成したDominic Giampaoloは、スワップなしでBeOSを実行しないように促しました。なぜそれが重要なのかを見てください。しかし、それは何らかの実装アーティファクトであったに違いありません。)

I / Oエラーに対するコードの応答もテストする必要があります。すべてのファイルをネットワーク共有に保存してから、アプリのワークロードが高いときにネットワークケーブルを取り外してください。同様に、ネットワーク経由で通信している場合は、ケーブルを外します-またはワイヤレスをオフにします。

私が特に腹立たしいのは、堅牢なJavascriptコードを持たないWebサイトです。Facebookのページには何十もの小さなJavascriptファイルがロードされますが、いずれかがダウンロードに失敗すると、ページ全体が壊れます。たとえば、ダウンロードを再試行するなど、何らかのフォールトトレランスを提供するか、スクリプトの一部がダウンロードされなかった場合に何らかの合理的なフォールバックを提供する何らかの方法が必要です。

大きな重要なファイルを作成している最中に、デバッガーまたは* NIXで「kill -9」を使用してアプリを強制終了してみてください。アプリが適切に設計されている場合、ファイル全体が書き込まれるかまったく書き込まれないか、または部分的にしか書き込まれていない場合、書き込まれたものは破損せず、保存されたデータは完全に使用可能ですファイルの再読み込み時にアプリ。

データベースには常にフォールトトレラントディスクI / Oがありますが、他の種類のアプリにはほとんどありません。ジャーナリングされたファイルシステムは、停電やクラッシュが発生した場合のファイルシステムの破損を防ぎますが、エンドユーザーデータの破損や損失を防ぐために何もしません。これはユーザーアプリケーションの責任ですが、データベース以外はほとんどフォールトトレランスを実装しません。


1
他の人からのサポートを必要としない実用的なアドバイスを多数+1。私が追加する唯一のことは、assertはコードにバグなければ失敗しない条件を文書化してチェックすることです。重要なファイルが見つからない、無効な入力など、「不運」が原因で失敗する可能性のあることを断言しないでください。
Ian Goldby

10

コードのテストを見るとき、通常、一連の思考プロセスを実行します。

  1. この「もの」をテスト可能なサイズのチャンクに分割するにはどうすればよいですか?テストしたいものだけを分離するにはどうすればよいですか?どのスタブ/モックを作成する必要がありますか?
  2. 各チャンクについて:このチャンクをテストして、適切な一連の正しい入力に正しく応答することを確認するにはどうすればよいですか?
  3. 各チャンクについて:チャンクが正しくない入力(NULLポインター、無効な値)に正しく応答することをテストするにはどうすればよいですか?
  4. 境界をテストする方法(たとえば、値が符号付きから符号なし、8ビットから16ビットなど)。
  5. テストはコードをどの程度カバーしていますか?見逃した条件はありますか?[これは、コードカバレッジツールに最適な場所です。]逃したコードや実行できないコードがある場合、本当に必要なのでしょうか。[それはまったく別の質問です!]

これを行うために私が見つけた最も簡単な方法は、コードとともにテストを開発することです。コードの断片さえ書いたらすぐに、テストを書きたいと思います。数千行のコードを簡単な循環的コードの複雑さでコーディングした後、すべてのテストを実行しようとするのは悪夢です。数行のコードを追加した後、さらに1つまたは2つのテストを追加するのは本当に簡単です。

ところで、あなたが働いている会社や同僚が単体テストやTDDを行っていないからといって、特に禁止されていない限り、試してはいけないという意味ではありません。おそらく、それらを使用して堅牢なコードを作成することは、他の人にとって良い例でしょう。


5

他の回答で与えられたアドバイスに加えて、静的分析ツール(ウィキペディアにはさまざまな言語の静的分析ツールのリストがあります)を使用して、テストを開始する前に潜在的な欠陥を見つけ、関連するいくつかのメトリックを監視することをお勧めします循環的複雑度ハルステッド複雑度尺度、凝集度と結合などのコードのテスト容易性(これらをファンインおよびファンアウトで測定できます)。

テストするのが難しいコードを見つけて、テストしやすくすると、テストケースを書くのが簡単になります。また、欠陥を早期に発見することで、品質保証プラクティス全体(テストを含む)に価値が追加されます。ここから、単体テストツールとモックツールに慣れることで、テストを簡単に実装できるようになります。


1
循環的複雑度の場合は+1。「可能なすべてのパスをたどってバグを見つけるのは本当に難しいと思う」とは、OPのコードをより小さく、より複雑でないチャンクに分割する必要があることを意味します。
トビー

@Tobyええ、だからこそ私は静的解析を導入することにしました。コードをテストできない場合、問題が発生します。また、コードに1つの問題がある場合、他にも問題がある可能性があります。ツールを使用して潜在的な警告フラグを見つけ、評価し、必要に応じて修正します。テスト可能なコードが増えるだけでなく、コードも読みやすくなります。
トーマスオーエンズ

3

真理値表の使用法を調べて、コード内のすべての潜在的なパスを定義するのに役立ちます。複雑な機能のすべての可能性を説明することは不可能ですが、既知のすべてのパスに対して処理を確立したら、elseケースの処理を確立できます。

ただし、この特定の能力のほとんどは、経験によって学習されます。特定のフレームワークをかなりの時間使用した後、コードの一部を見て、小さな変更が重大なエラーを引き起こす可能性のある場所を確認できる動作のパターンとイヤーマークを確認し始めます。これであなたの適性を高めるために私が考えることができる唯一の方法は練習です。


3

あなたがユニットテストを必要としないと言ったなら、私はあなた自身のコードを手作業で壊そうとするよりも良いアプローチを見ません。

コードを限界までプッシュしてみてください。たとえば、境界の制限を超える関数に変数を渡そうとします。ユーザー入力をフィルタリングすることになっている関数はありますか?文字の異なる組み合わせを入力してみてください。

ユーザーの視点を考慮してください。アプリケーションまたは関数のライブラリを使用するユーザーの1人になるようにしてください。


1
ユーザーの観点から物事を見ることに言及するための+1。
マシューロダトゥス

3

しかし、現在の雇用主にはテスターがいません。同僚はこれにかなり優れているようです

あなたの同僚は、TDDやユニットテストに従わず、バグを発生させないために、本当に例外的でなければなりません。

あなたの同僚は許可されているよりも多くのテストを行っていると推測していますが、この事実は経営者に知られていないため、管理者は真のテストが実行されておらずバグ数が少ないという印象を受けるため、結果として組織が苦しんでいますテストは重要ではなく、時間はスケジュールされません。

同僚と話をして、彼らがどのような単体テストを行っているのかを把握し、それをエミュレートしてください。後で、ユニットテストとTDD属性のより良い方法のプロトタイプを作成し、これらの概念をチームにゆっくりと導入して、導入を容易にします。


2
  • コードを書く前にテストを書いてください。
  • テストでキャッチされなかったバグを修正するときはいつでも、そのバグをキャッチするテストを作成してください。

あなたの組織が完全にカバーしていない場合でも、あなたが書いたものをカバーすることができるはずです。プログラミングにおける非常に多くのことと同様に、何度も何度もそれを行う経験は、それを効率的にするための最良の方法の1つです。


2

他のすべてのコメントに加えて、同僚は非幸福度テストを書くのが得意だと言うので、いくつかのテストを書く際に同僚とペアリングするように依頼してはいかがでしょうか。

学ぶための最良の方法は、それがどのように行われたかを見て、そこから学んだことを取り入れることです。


2

ブラックボックステスト!テストを念頭に置いてクラス/メソッドを作成する必要があります。テストはソフトウェアの仕様に基づいており、シーケンス図で(ユースケースを介して)明確に定義する必要があります。

テスト駆動開発をしたくないかもしれないので...

すべての着信データに対して入力検証を行います。誰も信用しないでください。.netフレームワークは、無効な引数、null参照、および無効な状態に基づいて多くの例外をスローしています。すでにUIレイヤーで入力検証を使用することを考えているはずなので、ミドルウェアでも同じトリックです。

ただし、実際には何らかの自動テストを行う必要があります。そのようなものは命を救います。


2

私の経験では

テストユニット、それが完全に自動でない場合、それは役に立たない。先のとがった髪のボスが買えるようなものです。テストユニットは、テストプロセスを自動化する時間(およびお金)を節約することを約束したためです。しかし、一部のテストユニットツールは反対のことを行い、プログラマーに何らかの奇妙な方法で作業させ、他のユーザーに拡張テストの作成を強制します。ほとんどの場合、作業時間は節約されませんが、QAから開発者への移行時間が長くなります。

UMLはもう1つの無駄です。単一のホワイトボード+ペンで同じことを、より安く、素早く行うことができます。

ところで、コーディングを上手にするにはどうすればいいですか(そしてバグを避けますか)?

  • a)原子性。1つの単純な(またはいくつかの単一のタスク)を行う1つの関数。理解しやすいため、追跡も解決も簡単です。

  • b)相同性。たとえば、ストアプロシージャを使用してデータベースを呼び出す場合は、残りのコードを実行します。

  • c)「クリエイティブコード」を特定、削減、分離する。ほとんどのコードはほとんどコピー&ペーストです。創造的なコードは反対で、新しいコードであり、プロトタイプとして機能しますが、失敗する可能性があります。このコードは論理的なバグが発生しやすいため、コードを削減、分離、特定することが重要です。

  • d)「シンアイス」コードは、「不正」(または潜在的に危険)であるが、まだ必要であることがわかっているコードです(たとえば、マルチタスクプロセスの安全でないコード)。可能な場合は避けてください。

  • e)ブラックボックスコードを避けます。これには、ユーザー(フレームワークなど)によって実行されないコードと正規表現が含まれます。この種のコードのバグを見逃すのは簡単です。たとえば、Jbossを使用するプロジェクトで働いていて、Jbossで1つではなく10のエラーが見つかりました(最新の安定バージョンを使用)。それらを見つけるのはPITAでした。特別にHibernateを避けてください、それは実装を隠すため、バグです。

  • f)コードにコメントを追加します。

  • g)バグのソースとしてのユーザー入力。それを識別します。たとえば、SQLインジェクションはユーザー入力によって引き起こされます。

  • h)チームの悪い要素を特定し、割り当てられたタスクを分離します。一部のプログラマーは、コードを台無しにする傾向があります。

  • i)不要なコードを避けます。たとえば、クラスインターフェイスを必要とする場合は、それを使用します。それ以外の場合は、無関係なコードを追加しないでください。

a)とb)が重要です。たとえば、システムで問題が発生しました。ボタンをクリック(保存)しても、フォームが保存されませんでした。その後、チェックリストを作成しました:

  • ボタンは機能しますか?...はい。
  • データベースは何かを保存しますか?いいえ、エラーは中間段階にありました。
  • 次に、データベースに格納するクラスが機能しますか?いいえ<-OK、エラーが見つかりました。(データベースの許可に関連していました)。次に、この手順だけでなく、同じことを行うすべての手順をチェックしました(コードの相同性のため)。バグを追跡するのに5分かかり、それを解決するのに1分かかりました(および他の多くのバグ)。

そして補足

ほとんどの場合、QAは(開発の別のセクションとして)吸うので、プロジェクトで働いていなければ役に立たない。彼らはいくつかの一般的なテストを行いますが、それ以外のことはほとんどしません。ほとんどの論理バグを特定することはできません。私の場合、私は有名な銀行で働いていました。プログラマーがコードを完成させ、QAに送信しました。QAはコードを承認し、本番環境に投入されました...その後、コードは失敗しました(壮大な失敗)。誰が非難されたか知っていますか?はい、プログラマー。


2

テスターとプログラマーは異なる角度から問題に直面しますが、両方の役割は機能を完全にテストし、バグを見つける必要があります。役割が異なるところに焦点が当てられています。古典的なテスターは、アプリケーションを外部(ブラックボックス)からのみ見ます。彼らはアプリの機能要件の専門家です。プログラマーは、機能要件とコードの両方の専門家であることが期待されます(ただし、コードにより重点を置く傾向があります)。

(プログラマーが要件の専門家であることが明示的に期待されているかどうかは組織に依存します。それにもかかわらず、暗黙の期待があります。

この二重の専門家の役割は、プログラマの心に負担をかけ、最も経験豊富な人を除き、要件の習熟度を低下させる可能性があります。アプリケーションのユーザーを考慮するために、精神的にギアをシフトする必要があることがわかりました。これが私を助けるものです:

  1. デバッグ ; コードにブレークポイントを設定し、アプリケーションを実行します。ブレークポイントに到達したら、アプリケーションを操作しながら行をステップスルーします。
  2. 自動テスト ; コードをテストするコードを書きます。これは、UIの下の階層でのみ役立ちます。
  3. テスターに​​ついて知りましょう。彼らはあなたよりもアプリケーションをよく知っているかもしれないので、彼らから学びましょう。あなたのアプリケーションの弱点は何か、彼らはバグを見つけるためにどのような戦術を使っているのかを尋ねてください。
  4. ユーザーを知る ; ユーザーの靴の中を歩くことを学ぶ。機能要件は、ユーザーの指紋です。多くの場合、ユーザーがアプリケーションについて知っている多くのことがありますが、それらは機能要件に明確に反映されていない場合があります。ユーザーをよりよく理解すると(実際の世界での彼らの仕事の性質と、アプリケーションがどのように役立つのか)、アプリケーションがどうあるべきかをよりよく理解できます。

2

あなたは2つの面で働きたいと思います。1つは政治的であり、組織にあるレベルのテストを採用させる(時間の経過とともにより多くのテストを採用することを期待して)。職場外のQAエンジニアに相談してください。QAブックのリストを検索します。つつく関連するWikipediaの記事を。QAの原則と実践に精通します。このことを学習することで、組織内でできる限り説得力のある事例を作成する準備ができます。質の高いQA部門が存在し、組織にかなりの価値をもたらします。

個々の開発者として、自分の仕事で使用する戦略を採用します。コードとテストを共同開発してTDDを使用します。テストを明確に維持します。なぜそうするのかと聞かれると、リグレッションを防いでいると言うことができ、思考プロセスをより良く整理することができます(どちらも当てはまります)。テスト可能なコードを書くには、それを学ぶ芸術があります。仲間の開発者にとって良い例になります。

部分的に私はここで自分自身に説教しています。なぜなら、私は自分がすべきだと思っている以上にこのようなことをやらないからです。

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