web-dev-qa-db-ja.com

単体テストを高速に実行するにはどうすればよいですか?

私たちのプロジェクトでは、ほぼ1000のテストが完了し、チェックインに時間がかかるため、人々がチェックインを実行する前にそれらの実行に煩わされることがなくなりました。せいぜい、彼らは変更したコードに関連するテストを実行し、最悪の場合、テストせずにチェックインするだけです。

この問題は、ソリューションが120プロジェクトに成長し(通常、はるかに小さなプロジェクトを実行し、これはTDDを適切に実行するのは2回目です)、ビルド+テスト時間が約2〜3分に増加したことが原因であると思います。より小さいマシンで。

テストの実行時間を短縮するにはどうすればよいですか?テクニックはありますか?もっと偽造?偽物は少ないですか?たぶん、すべてのテストを実行するとき、より大きな統合テストは自動的に実行されるべきではありませんか?

編集:いくつかの回答に対する応答として、すでにCIとビルドサーバーを使用しています。これが、テストが失敗したことを知る方法です。問題(実際には症状)は、ビルドが失敗したというメッセージを受け取り続けることです。部分テストの実行は、ほとんどの人が行うことですが、すべてではありません。そしてテストに関して、それらは実際にはかなりよく作られています、彼らはすべてに偽物を使用し、IOはまったくありません。

40
Ziv

可能な解決策は、いくつかのフレーバーのバージョン管理ソフトウェアを使用して、テスト部分を開発マシンから継続的インテグレーション設定( Jenkins など)に移動することです( git 、- svn など...)。

新しいコードを記述する必要がある場合、指定された開発者は、リポジトリで実行しているすべてのことに対してブランチを作成します。すべての作業はこのブランチで行われ、コードのメイン行を混乱させることなく、いつでもブランチへの変更をコミットできます。

与えられた機能、バグ修正、または彼らが取り組んでいる他のすべてが完了したら、ブランチをトランクにマージして戻すことができます(ただし、実行したい場合)。すべての単体テストが実行されます。テストが失敗した場合、マージは拒否され、開発者に通知されるため、エラーを修正できます。

コミットが行われるときに、CIサーバーで各機能ブランチの単体テストを実行することもできます。このようにして、開発者はいくつかの変更を加え、コードをコミットし、サーバーがバックグラウンドでテストを実行する間、追加の変更や他のプロジェクトで作業を続けることができます。

このような設定を行うための1つの方法についての優れたガイドは、こちらにあります(git固有ですが、他のバージョン管理システムでも機能するはずです): http://nvie.com/posts/a-successful-git-branching-モデル/

50
Mike

ユニットテストの大部分は、それぞれ10ミリ秒未満かかるはずです。 「ほぼ千のテスト」を行うことは何もないで、実行にはたぶん数秒かかるはずです。

そうでない場合は、高度に結合されたintegrationテストの記述を停止し(コードに必要な場合を除く)、適切なユニットテストの記述を開始します(十分に分離されたコードと、フェイク/モック/スタブなどの適切な使用方法。この結合は、テストの品質と書き込みにかかる時間にも影響します。テストの実行時間を短縮するだけの問題ではありません。

33
Telastyn

私が同様の問題を解決するために使用したいくつかのアプローチがあります:

  1. 実行時間を確認し、最も遅いテストをすべて見つけてから、実行に非常に時間がかかる理由を分析してください
  2. 100個のプロジェクトがありますが、毎回それらをビルドしてテストする必要はありませんか?ナイトビルドでのみすべてのユニットテストを実行できますか? 毎日の使用のためにいくつかの「高速」ビルド構成を作成します。 CIサーバーは現在の開発プロセスの「ホット」な部分に関連する単体テストプロジェクトの限られたセットのみを実行します
  3. 可能な限りすべてをモックして分離する、可能な場合は常にディスク/ネットワークI/Oを回避する
  4. このような操作を分離できない場合、統合テストを行っているのでしょうか? 統合テストをナイトビルドのみにスケジュールする
  5. インスタンス/リソースへの参照を維持し、メモリを消費するすべての不定期のシングルトンをチェックします。これにより、すべてのテストの実行中にパフォーマンスが低下する可能性があります。

さらに、あなたはあなたの人生を簡単にし、テストをより速く実行するために次のツールを使うことができます

  1. ゲートコミット一部のCIサーバーは、ソースリポジトリにコードをコミットする前にビルドとテストを実行するように構成できます。誰かがすべてのテストを事前に実行せずにコードをコミットすると、失敗したテストも含まれ、拒否されて作成者に返されます。
  2. CIサーバーの構成テストを並行して実行するため:複数のマシンまたはプロセスを使用します。例は、pnunitおよび複数のノードを持つCI構成です。
  3. 継続的テストプラグイン開発者向け。コードの記述中にすべてのテストを自動的に実行します。
16
Akim

0。プログラマの言うことを聞いてください。

彼らがテストを実行していない場合、それはコスト(テストが実行されるのを待って、誤った失敗を処理する)が値(バグをすぐにキャッチする)よりも大きいと認識していることを意味します。コストを削減し、価値を高めれば、人々は常にテストを実行します。

1。テストの信頼性を100%にします。

偽陰性で失敗するテストがある場合は、すぐに対処してください。 100%の信頼性を保証するために必要なものは何でも、修正、変更、排除します。 (信頼性が低くても有用な一連のテストを個別に実行できることは問題ありませんが、テストの本体は信頼性がなければなりません。)

2。システムを変更して、すべてのテストが常に合格することを保証します。

継続的インテグレーションシステムを使用して、通過するコミットのみがmain/official/release/whateverブランチにマージされるようにします。

3。文化を変更して、100%テストに合格することを評価します。

100%のテストに合格し、main/official/release/whateverブランチにマージされるまで、タスクは「完了」しないことをレッスンに教えます。

4。テストを高速化します。

私は、テストに1秒かかるプロジェクトと、終日かかるプロジェクトに取り組んできました。テストの実行にかかる時間と私の生産性の間には強い相関関係があります。

テストの実行に時間がかかるほど、実行する頻度が少なくなります。つまり、加えた変更に関するフィードバックを得ることなく、より長く行くことができます。また、コミットの間隔が長くなることも意味します。より頻繁にコミットすると、マージが容易になる小さなステップになります。コミット履歴をたどるのは簡単です。履歴のバグを見つけるのは簡単です。ロールバックも簡単です。

コンパイルが毎回自動的に実行されるほど気にしないほど速く実行されるテストを想像してみてください。

テストを高速化するのは難しい場合があります(OPが求めていることです)。 デカップリングが重要です。モック/偽物は大丈夫ですが、モック/偽物を不要にするためにリファクタリングすることでもっとうまくできると思います。 http://arlobelshee.com/post/the-no-mocks-book で始まるArlo Belsheeのブログを参照してください。

5。テストを便利にする。

失敗してもテストが失敗しない場合、何が問題なのでしょうか。作成する可能性のあるバグをキャッチするテストを作成するように自分自身に教えます。これはそれ自体がスキルであり、多くの注目を集めます。

12
Jay Bazuzi

単体テストでは数分で問題ありません。ただし、テストには3つの主要なタイプがあることに注意してください。

  1. ユニットテスト-プロジェクトの残りの部分とは関係なく、各「ユニット」(クラスまたはメソッド)をテストします
  2. 統合テスト-通常はプログラムを呼び出して、プロジェクト全体をテストします。私が見たいくつかのプロジェクトはこれを回帰テストと組み合わせています。単体テストよりもモックが大幅に少ない
  3. 回帰テスト-テストスイートはエンドユーザーであるため、完了したプロジェクト全体をテストします。コンソールアプリケーションがある場合は、コンソールを使用してプログラムを実行およびテストします。内部テストをこれらのテストに公開することは決してなく、プログラムのエンドユーザーは(理論的には)回帰テストスイートを実行できるはずです(決して実行されません)。

これらは速度の順にリストされています。単体テストは迅速でなければなりません。彼らはすべてのバグをキャッチするわけではありませんが、プログラムがまともであると確信しています。単体テストは、3分以内または適切なハードウェアで実行する必要があります。あなたは1000個のユニットテストしか持っていないと言っています、そしてそれらは2〜3分かかりますか?まあ、それはおそらく大丈夫です。

確認すること:

  • ただし、単体テストと統合テストが別々であることを確認してください。統合テストは常に遅くなります。

  • 単体テストが並行して実行されていることを確認します。彼らが本当の単体テストであるかどうか彼らがそうしない理由はありません

  • 単体テストが「依存関係なし」であることを確認してください。彼らは決してデータベースやファイルシステムにアクセスすべきではありません

それ以外は、あなたのテストは今のところそれほど悪くないように聞こえます。ただし、参考までに、マイクロソフトチームの友人の1人は、まともなハードウェアで2分未満で実行される4,000の単体テストを持っています(これは複雑なプロジェクトです)。高速な単体テストが可能です。依存関係をなくすこと(そして必要なだけモックすること)がスピードを上げるための主なものです。

4
Earlz

開発者を Personal Software Process(PSP) でトレーニングし、より多くの規律を使用してパフォーマンスを理解および改善するのを支援します。コードを書くことは、キーボードで指を叩くこととは何の関係もありません。その後、コンパイルしてチェックインボタンを押します。

PSPはかつて、コードのコンパイルが長い時間(メインフレームでは数時間/日なので、誰もがコンパイラーを共有しなければならなかった)のプロセスであったときに非常に人気がありました。しかし、パーソナルワークステーションがより強力になったとき、私たちは全員このプロセスを受け入れるようになりました。

  1. 考えずにコードを入力する
  2. ビルド/コンパイルをヒット
  3. 構文を修正してコンパイルします
  4. テストを実行して、書き込んだ内容が実際に意味があるかどうかを確認します

タイプする前に考え、タイプしてから書いた内容を確認すると、ビルドとテストスイートを実行する前にエラーの数を減らすことができます。ビルドを1日に50回押すのではなく、1〜2回押すことを学んでください。そうすれば、ビルドとテストに数分かかることはそれほど問題になりません。

3
Bart Koopman

1つの可能な方法:ソリューションを分割します。ソリューションに100のプロジェクトがある場合、それはかなり管理できません。 2つのプロジェクト(AとBなど)が別のプロジェクト(Libなど)の共通コードを使用しているからといって、それらが同じソリューション内にある必要があるとは限りません。

代わりに、プロジェクトAとLibを使用してソリューションAを作成し、プロジェクトBとLibを使用してソリューションBを作成することもできます。

3
svick

私も同じような状況です。サーバーとの通信をテストする単体テストがあります。彼らは、タイムアウト、接続のキャンセルなどの動作をテストしています。すべてのテストセットは7分間実行されます。

7分は比較的短い時間ですが、すべてのコミットの前に行うことではありません。

自動UIテストのセットもあり、実行時間は2時間です。それはあなたがあなたのコンピュータで毎日走らせたいものではない。

じゃあ何をすればいいの?

  1. 通常、テストを変更してもあまり効果的ではありません。
  2. コミットする前に、関連するテストのみを実行してください。
  3. すべてのテストをビルドサーバーで毎日(または1日に数回)実行します。これにより、ニースコードカバレッジとコード分析レポートを生成することもできます。

重要なのは、バグを見つけることが重要であるため、すべてのテストを頻繁に実行する必要があることです。ただし、コミットの前にそれらを見つける必要は必ずしもありません。

2
Sulthan

問題の説明からコードベースを完全に理解することはできませんが、問題は2つあると言えます。

適切なテストを書くことを学ぶ。

あなたはあなたがほぼ千のテストをしていて、120のプロジェクトを持っていると言います。これらのプロジェクトのせいぜい半分がテストプロジェクトであるとすると、60のプロダクションコードプロジェクトに対して1000のテストがあります。 これにより、プロジェクトの約16〜17回のテストが可能になります!!!

それはおそらく、私が実動システムで約1から2クラスをカバーしなければならないテストの量です。したがって、各プロジェクトに1〜2つのクラスしかない場合(プロジェクトの構造が細かすぎる場合)は、テストが大きすぎて、カバーする範囲が多すぎます。これは、TDDを適切に行っている最初のプロジェクトだと言っています。たとえば、あなたが提示する数値は、これが事実ではないことを示しており、TDDプロパティを実行していません。

適切なテストを作成する方法を学ぶ必要があります。つまり、コードを最初からテスト可能にする方法を学ぶ必要があります。それを行うためのチーム内の経験を見つけることができない場合、私は外部からの助けを雇うことをお勧めします。テスト可能なコードの記述を学ぶために2〜3か月にわたってチームを支援する1人または2人のコンサルタントの形で、小さな最小限の単体テスト。

比較として、私が現在取り組んでいる.NETプロジェクトでは、約500の単体テストを10秒未満で実行できます(ハイスペックマシンでさえ測定されていません)。それらがあなたの数字であるならば、あなたはこれらを時々ローカルで実行することを恐れません。

プロジェクト構造の管理について学びます。

ソリューションを120のプロジェクトに分割しました。それは私の基準では驚異的な量のプロジェクトです。

そのため、実際にその量のプロジェクトを行うことが理にかなっている場合(そうではないと感じますが、質問でこれを適切に判断するための十分な情報が提供されていません)、プロジェクトをより小さなコンポーネントに分割する必要があります。個別にビルド、バージョン管理、デプロイできます。そのため、開発者がテストスイートのユニットを実行する場合、開発者は現在取り組んでいるコンポーネントに関連するテストを実行するだけで済みます。ビルドサーバーは、すべてが正しく統合されていることを確認する必要があります。

しかし、プロジェクトを複数のコンポーネントに分割してビルド、バージョン管理、個別にデプロイするには、経験上、非常に成熟した開発チーム、つまり私があなたのチームよりも成熟しているチームが必要です。

しかし、いずれにしても、プロジェクトの構造について何かする必要があります。プロジェクトを個別のコンポーネントに分割するか、プロジェクトのマージを開始します。

120のプロジェクトが本当に必要かどうか自問してみてください。

pS NCrunchを調べてみてください。これは、バックグラウンドで自動的にテストを実行するVisual Studioプラグインです。

1
Pete

私が見た問題:

a)IOCを使用して、テスト要素を構築します。70秒-> 7秒、コンテナを削除します。

b)すべてのクラスをモックアウトしない。単体テストを単一の要素に保ちます。いくつかのクラスをくまなく調べるテストを見てきました。これらは単体テストではなく、壊れる可能性がはるかに高くなります。

c)それらをプロファイリングして、何が起こっていたかを調べます。コンストラクターが不要なものをビルドしていたので、ローカライズして実行時間を短縮しました。

d)プロファイル。おそらくコードはそれほど良くなく、レビューからいくらかの効率を得ることができます。

e)依存関係を削除します。テスト実行可能ファイルを小さくしておくと、ロードにかかる時間が短縮されます。インターフェイスライブラリとIOCコンテナを使用して最終的なソリューションを実行しますが、メインのテストプロジェクトではインターフェイスライブラリのみを定義する必要があります。これにより、分離が保証され、テストがより簡単になり、テストも行われますフットプリントが小さくなります。

0
Waratah

通常、JUnitテストは高速ですが、実行に時間がかかるものもあります。

たとえば、データベーステストは通常​​、初期化と終了に数時間かかります。

数百のテストがある場合、たとえそれらが高速であっても、その数のために実行に長い時間が必要です。

できることは:

1)重要なテストを特定します。ライブラリの最も重要な部分のものと、変更後に失敗する可能性が最も高いもの。コンパイル時に常に実行されるのはこれらのテストのみです。一部のコードが頻繁に破損している場合、実行に時間がかかってもそのテストは義務的である必要があります。反対に、ソフトウェアの一部が問題を引き起こさなかった場合は、各ビルドのテストを安全にスキップできます。

2)バックグラウンドですべてのテストを実行する継続的インテグレーションサーバーを準備します。 1時間ごとにビルドするか、毎コミット後にビルドするかは、あなた次第です(2番目は、コミットが問題を引き起こした人を自動的に検出したい場合にのみ意味があります)。

0
Danubian Sailor

私はあなたの苦痛を感じ、ビルド速度を大幅に改善できるいくつかの場所に出くわしました。ただし、私がお勧めすることの数は、ビルドが最も長くかかっている場所を特定するために詳細にmeasureすることです。たとえば、約30のプロジェクトを含むビルドがあり、実行に1分強かかります。ただし、それは全体像の一部にすぎません。また、どのプロジェクトの構築に最も時間がかかるかを知っているので、私の取り組みに集中できます。

ビルド時間を食うもの:

  • パッケージのダウンロード(Nuget for C#、Maven for Java、Gem for Rubyなど)
  • ファイルシステム上の大量のファイルのコピー(例:GDALサポートファイル)
  • データベースへの接続を開く(ネゴシエーションのために、接続ごとに1秒以上かかるものもあります)
  • リフレクションベースのコード
  • 自動生成されたコード
  • 例外を使用してプログラムフローを制御する

モックライブラリはリフレクションを使用するか、バイトコードライブラリを使用してコードを挿入してモックを生成します。非常に便利ですが、テスト時間を消費します。テストのループ内でモックを生成している場合、ユニットテストに測定可能な時間を追加する可能性があります。

問題を修正する方法があります。

  • データベースを含むテストを統合に移動します(つまり、CIビルドサーバー上のみ)
  • テストのループでモックを作成しないでください。実際には、テストのループを完全に回避してください。その場合、おそらくパラメーター化されたテストを使用して同じ結果を得ることができます。
  • 大規模なソリューションを個別のソリューションに分割することを検討してください

ソリューションに100を超えるプロジェクトが含まれる場合、ライブラリコード、テスト、およびアプリケーションコードの組み合わせがあります。各ライブラリは、関連するテストを備えた独自のソリューションにすることができます。 Jet Brains Team City は、Nugetサーバーとしても機能するCIビルドサーバーです。これが唯一のサーバーではないことは間違いありません。これにより、頻繁に変更されない可能性があるライブラリを独自のソリューション/プロジェクトに移動し、Nugetを使用してアプリケーションコードの依存関係を解決する柔軟性が得られます。小さいソリューションは、ライブラリに変更をすばやく簡単に加えることができ、メインソリューションのメリットを享受できることを意味します。

0
Berin Loritsch