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


40

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

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

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

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


8
より良いハードウェアを入手しますか?ハードウェアはプログラマーの時間に比べて安価です。
ブライアンオークリー

18
あなたはすでにあなたの質問で解決策を暗示しています:変更されたコードに関連するテストのみを実行してください。QA /リリースサイクルの一部として、テストスイート全体を定期的に実行します。とは言っても、2〜3分は多くの時間とは思えないので、開発チームが頻繁にチェックインしている可能性があります。
ロバートハーヴェイ

3
パフォーマンスコストの発生源を特定するための最初のベンチマーク。いくつかの高価なテストがありますか、それともテストの量は膨大ですか?特定のセットアップは高価ですか?
CodesInChaos

13
くそー、私たちのテストがたった2〜3分だったらいいのに。すべてのユニットテストを実行するには、25分かかります。統合テストはまだありません。
イズカタ

4
2〜3分?うん 私たちは...時間に実行することができます
ロディ冷凍ピーズの

回答:


51

可能な解決策は、ある種のバージョン管理ソフトウェア(gitsvnなど)を使用して、テスト部分を開発マシンから継続的な統合セットアップ(たとえばJenkins)に移動することです。

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

指定された機能、バグ修正、または作業中のその他の作業が完了すると、そのブランチはすべてのユニットテストが実行されるトランクにマージされます(または、それを行うことを好みます)。テストが失敗した場合、マージは拒否され、エラーを修正できるように開発者に通知されます。

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

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


15
この。開発者は「のチェックを行う前に(ユニットテスト)を実行していると悩ま停止した」場合は、あなたのCIのセットアップは、それらを実行することにしたいにチェック。
Carson63000

+1:テストをモジュール化することで、さらに改善されます。特定のモジュール/ファイルが最後の実行以降変更されていない場合、それをテストする責任があるテストを再実行する理由はありません。1つのファイルが変更されたからといって、すべてを再コンパイルしないメイクファイルのようなもの。これにはいくつかの作業が必要になる場合がありますが、おそらくよりクリーンなテストも提供されます。
レオ

分岐方法論はTFSで機能しますか?TFSを使用してC#を記述し、TFSでの分岐はgitよりもわかりにくいです。私たちは決して分岐しないので、この考えは拒否されるとさえ信じています。
Ziv

TFSの使用経験はありません。しかし、私はポストの1にsimlilar分岐戦略を示しているようだマイクロソフトから、このガイドに遭遇することができました: msdn.microsoft.com/en-us/magazine/gg598921.aspx
マイク

33

ユニットテストの大部分は、それぞれ10ミリ秒未満かかります。「ほぼ1000回のテスト」を行うことは何もありませ。実行には数秒かかるでしょう

そうでない場合は、高度に結合された統合テストの作成を停止し(コードで必要な場合を除き)、適切な単体テストの作成を開始する必要があります(適切に分離されたコードとフェイク/モック/スタブなどの適切な使用から開始)。このカップリングは、テストの品質とテストの作成にかかる時間にも影響を与えるため、テストの実行時間を短縮するだけではありません。


30
まあ、おそらく統合テストや他の非ユニット自動化テストを書くのをやめるべきではありません。単体テストと混同しないでください。また、部分的に速度が遅いという理由もあるため、それらを分離しておく必要があります。

2
これらが統合テストのように見えることは正しいです。
トムスクワイアズ

9
この答えは非生産的です。まず、それは不合理な期待を設定します。単体テストフレームワーク自体にはオーバーヘッドがあります。各テストの所要時間が1ミリ秒未満であることは、1000のテストの所要時間が数秒未満であることを意味するものではありません。OPのテストスイート全体が2〜3分で完了することは、ほとんどの方法で非常に良い兆候です。
rwong

6
@rwong-すみません、でたらめを呼び出します。私が得た測定基準は、私が利用できる2つの異なる専門プロジェクトを実行したことです。1つは〜300テスト、もう1つは〜30000テスト、およびテストランタイムの確認です。1000未満のテストで2〜3分かかるテストスイートはひどく、テストが十分に分離されていないことを示しています。
テラスティン

2
@rwong Telastynと同じように、ここにデータポイントがあります。かなり多くの理想的ではないテストでも、テストフレームワーク(py.test)はバックグラウンドで大量の魔法を行い、すべてが純粋なPythonコード( "100x私のプロジェクトで約500テストを実行すると、数年前の遅いネットブックで6秒もかかりません。この図は、テストの数がほぼ線形です。起動時のオーバーヘッドはいくらかありますが、すべてのテストで償却され、テストごとのオーバーヘッドはO(1)です。

16

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

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

さらに、次のツールを使用して、人生を楽にし、テストをより速く実行できます。

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

12

0.プログラマーの話を聞いてください。

テストを実行していない場合、コスト(テストが実行されるのを待って、誤った障害に対処する)が値よりも大きい(バグをすぐに見つける)ことを認識しています。コストを削減し、価値を高めると、人々は常にテストを実行します。

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

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

2.システムを変更して、すべてのテストが常に合格するようにします。

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

3.テストを100%合格するように文化を変更します。

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

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

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

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

コンパイルするたびに自動的に実行されることを気にしないほど高速に実行されるテストを想像してください。

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

5.テストを便利にします。

失敗したときにテストが失敗しない場合、そのポイントは何ですか?作成する可能性が高いバグをキャッチするテストを作成するように自分で教えてください。これはそれ自体がスキルであり、多くの注意が必要です。


2
特にポイント3と1に強く同意します。開発者がテストを実行していない場合、テストが壊れているか、環境が壊れているか、またはその両方です。ポイント1が最小です。偽の失敗は、欠落しているテストよりも悪いです。人々が受け入れることを学ぶのは失敗するからです。障害が許容されると、それは広がり、100%合格に戻り、100%合格を期待するために多大な努力を要します。今日からこれを修正してください
ビルIV 14

そして、どうして#5に同意できないのでしょうか?!?1と3、または2と4に加えて!とにかく、すべての周りの素晴らしい答え。
つの過去の真夜中

4

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

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

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

確認事項:

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

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

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

それ以外は、あなたのテストは今のところひどく聞こえません。ただし、参考までに、Microsoftチームの友人の1人は、まともなハードウェアで2分未満で実行される4,000のユニットテストを持っています(そして、それは複雑なプロジェクトです)。素早いユニットテストが可能です。依存関係を排除する(および必要なだけモックする)ことは、速度を得るための主なものです。


3

開発者に個人用ソフトウェアプロセス(PSP)のトレーニングを行い、より多くの規律を使用してパフォーマンスの理解と向上を支援します。コードを書くことは、キーボードを指でたたくとは関係ありません。その後、コンパイルを押してチェックインボタンを押します。

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

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

入力する前に考え、次に入力した後、書いた内容を確認すれば、ビルドおよびテストスイートを実行する前にエラーの数を減らすことができます。ビルドを1日に50回押すのではなく、1〜2回押します。そうすれば、ビルドとテストに数分かかることが少なくなります。


2
私はあなたのリストに大いに同意しますが、絶対に「1日に2回だけビルドを実行することは50回より良い」ということには同意しません。
Doc Brown

3

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

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


2

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

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

また、一連の自動化されたUIテストがあり、実行時間は2時間です。コンピューターで毎日実行したいものではありません。

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

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

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


1
サーバーと通信するテストについて:サーバーと通信する場合、それは実際には単体テストではなく、より高いものです。もし私があなただったら、私はユニットテストを分離し(クイックに実行するべきです)、少なくともコミットする前にそれらを実行します。そうすれば、コードがコミットされる前に、少なくとも素早いもの(サーバーと話す必要のないもの)を邪魔にならないようにできます。
マイケルコーン

@MichaelKohne誰かがそれを見つけるだろうと知っていた。私はそれらが正確に単体テストではないことを知っていますが、それらは同じ目的を果たします、それはあなたがそれらにどのように名前を付けるかについてだけです。
スルタン

1
ほとんどの場合、名前の付け方についてですが、違いを覚えておくのは良いことです(使用する名前は何でも)。区別しないと、(私の経験では)開発者は高レベルのテストを書く傾向があります。その時点で、抽象化と結合に理にかなっていることを強制するテストを取得できません。
マイケル

1

問題の説明はコードベースの完全な洞察を与えるものではありませんが、私はあなたの問題が二重にあると安全に言えると思います。

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

ほぼ1,000のテストがあり、120のプロジェクトがあると言います。これらのプロジェクトの最大で半分がテストプロジェクトであると仮定すると、60の実動コードプロジェクトに対して1000のテストがあります。それはあなたに約16-17のテストprを与えます。プロジェクト!!!

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

適切なテストを書くことを学ぶ必要があります。これはおそらく、最初にコードをテスト可能にする方法を学ぶ必要があることを意味します。チーム内でそのような経験が見つからない場合は、外部からの助けを借りることをお勧めします。たとえば、テスト可能なコードを書くことを学ぶために2〜3か月にわたってチームを支援する1〜2人のコンサルタントの形で最小ユニットテスト。

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

プロジェクト構造の管理を学びます。

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

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

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

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

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

ps NCrunchをチェックしてみてください。バックグラウンドでテストを自動的に実行するVisual Studioプラグインです。


0

通常、JUnitテストは迅速に行われますが、実行に時間がかかる場合もあります。

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

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

できることは:

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

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


0

私が見た問題:

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

b)すべてのクラスをモックアウトしていない。単体テストの単体テストを保持します。私はいくつかのクラスを駆け巡るテストを見てきました。これらは単体テストではなく、はるかに壊れやすい可能性があります。

c)何が起こっているのかを知るためにプロファイルします。コンストラクターは必要のないものを構築していることがわかったので、ローカライズして実行時間を短縮しました。

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

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


0

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

ビルド時間を使い果たすもの:

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

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

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

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

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


-1

テスト環境はどこでも実行できますか?可能であれば、クラウドコンピューティングを使用してテストを実行します。N個の仮想マシン間でテストを分割します。単一のマシンでテストを実行する時間がT1秒である場合、テストを実行する時間は分割され、T2はT2 = T1 / Nに近づく可能性があります。(各テストケースにほぼ同じ時間がかかると仮定します。)そして、VMを使用するときに料金を支払うだけで済みます。そのため、24時間年中無休のラボに多数のテストマシンがありません。(私が仕事をしている場所でこれができるようにしたいのですが、特定のハードウェアに縛られています。VMがいません。)

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