統合テストをどのようにスケーリングしますか?


21

現在の製品で増加している統合テストの規模を拡大するための技術と戦略を調査しており、開発およびCIプロセスの一部として(人間的に)残ることができます。

約200以上の統合テストで、(デスクトップ開発マシン上で)完全なテスト実行を完了するために1時間のマークをすでに打っています。これは、定期的なプッシュプロセスの一部としてスイート全体を実行することを許容する開発者の能力に悪影響を及ぼしています。それは、それらをうまく作成することについて規律付けられる動機に影響しています。主要なシナリオのみを前面から背面まで統合テストし、テストを実行するたびにゼロから構築される本番環境をミラーリングする環境を使用します。

実行に時間がかかるため、テスト実行の集中度に関係なく、ひどいフィードバックループが発生し、テスト実行の終了をマシンで待機する多くの無駄なサイクルが発生します。フローと進捗、健全性、持続可能性に対するより高価な悪影響を決して気にしないでください。

この製品の速度が低下し始める前に、10倍以上の統合テストが行​​われると予想されます(実際にはわかりませんが、機能に関してはまだ始まったばかりではありません)。私たちは、数百または数千の統合テストに合理的に期待する必要があります。

明確に言うと、これがユニットテストと統合テストの議論にならないようにしようとすることです。この製品では、TDDを使用した単体テストと統合テストの両方を行っています。実際、アーキテクチャのパターンを他の領域に変更する際に重大な変更を導入する場所を検証する必要があるため、私たちが理にかなっているサービスアーキテクチャのさまざまな層で統合テストを行いますシステム。 

技術スタックについて少し。現在、テストをエンドツーエンドで実行するために、(CPUとメモリを集中的に使用する)エミュレーション環境でテストしています。これは、noSqlバックエンド(ATS)の前にあるAzure REST Webサービスで構成されています。Azureデスクトップエミュレーター+ IISExpressで実行することにより、運用環境をシミュレートしています。開発マシンごとに1つのエミュレータと1つのローカルバックエンドリポジトリに制限されています。

また、同じエミュレート環境で同じテストを実行するクラウドベースのCIもあり、現在のCIプロバイダーではクラウドでのテスト実行に2倍の時間がかかります(2時間以上)。ハードウェアパフォーマンスの観点からクラウドCIプロバイダーSLAの制限に達し、テスト実行時の許容値を超えました。公平を期すために、仕様は悪くはありませんが、社内の汚いデスクトップマシンの半分の品質であることは明らかです。

テストの論理グループごとにデータストアを再構築し、テストデータをプリロードするテスト戦略を使用しています。データの整合性を包括的に保証しながら、これにより各テストに5〜15%の影響が追加されます。したがって、製品開発のこの時点では、そのテスト戦略を最適化することはほとんど得られないと考えています。 

長いことと短いことです。各テストのスループットを最適化できましたが(それぞれ30%〜50%であっても)、数百のテストで近い将来に効果的にスケーリングすることはありません。1時間は今でも人間の許容範囲をはるかに超えています。それを維持するには、全体のプロセスを1桁改善する必要があります。

そのため、テスト時間を大幅に短縮するために使用できる手法と戦略を調査しています。

  • より少ないテストを書くことはオプションではありません。このスレッドで議論しないでください。
  • 非常に高価ですが、より高速なハードウェアを使用することは間違いなくオプションです。
  • 並列の別個のハードウェアでテスト/シナリオのグループを実行することも、間違いなく推奨されるオプションです。
  • 開発中の機能とシナリオに関するテストのグループ化を作成することはもっともらしいですが、最終的には、システムが変更の影響を受けないという完全なカバレッジまたは信頼性を証明することはできません。 
  • デスクトップエミュレーターで実行する代わりに、クラウドスケールのステージング環境で実行することは技術的に可能ですが、テスト実行に展開時間を追加し始めます(テスト実行の開始時に、〜20分ごとに展開します)。
  • システムのコンポーネントを独立した対数部分に分割することはある程度妥当ですが、コンポーネント間の相互作用は時間とともに増加することが予想されるため、その上で限られた燃費が期待されます。(つまり、変更は他の人に予期しない形で影響を与える可能性が高い-システムがインクリメンタルに開発されるときによく起こるように)

この分野で他の人がどの戦略(およびツール)を使用しているかを知りたかった。

(他の人が特定の技術セットを使用してこの種の困難に直面している可能性があると信じる必要があります。)

[更新:2016年12月16日:結果の議論のため、CI並列テストにより多くの投資をすることになりました:http : //www.mindkin.co.nz/blog/2015/12/16/16-jobs]


この投稿を作成してから、私はnCrunch(単体テストに広く使用している)が私たちに戦術を提供できるツールであるかもしれないことを調査しました。明らかに、テストをリモートマシンに送り、並行して実行する機能があります。それでは、統合テストのグループと、高スペックのクラウドマシンの複数のインスタンスを特定することを試してみることはできますか?nCrunchは、これがこの機能の正確な意図だと主張しています。他の誰かがこれを試しましたか?
ジェズサントス

これは、統合テストとは何か、統合テストではないもの、そして単体テストと統合テストについての人々の誤解についての議論に降りかかっているようです。
ジェズサントス

回答:


9

統合テストを実行するのに5時間(30台のマシンで)かかった場所で働きました。コードベースをリファクタリングし、代わりに新しいものの単体テストを行いました。単体テストには30秒かかりました(1台のマシンで)。ああ、バグも減りました。そして、詳細なテストで何が壊れたのかを正確に知ってからの開発時間。

簡単に言えば、そうではありません。完全な統合テストは、コードベースが大きくなるにつれて指数関数的に大きくなります(コードが増えるとテストが増え、コードが増えると、「統合」が進むにつれてすべてのテストの実行時間が長くなることを意味します)。フィードバックループが存在しないため、「時間」の範囲内にあるものは、継続的インテグレーションの利点のほとんどを失うと主張します。桁違いの改善でさえ、あなたを良くするのに十分ではありません-そして、それはあなたをスケーラブルにするためにどこにも近くありません。

したがって、統合テストを最も広く最も重要な煙テストにまで削減することをお勧めします。その後、夜間または連続的でない間隔で実行できるため、パフォーマンスの必要性が大幅に軽減されます。コードを追加するだけで直線的に成長する単体テスト(テストが増加し、テストごとのランタイムは増加しません)は、規模を拡大する方法です。


同意する。単体テストはよりスケーラブルであり、より高速なフィードバックループをサポートします。
ブランドン

8
あなたはその点を見逃しているかもしれません。OPは、広範な統合テストと問題の統合テストをすでに実行しています。単体テストは、統合テストの代わりになることはありません。さまざまなツール、さまざまなプラクティス、さまざまな目的、さまざまな結果。どちらか一方の問題ではありません。
ジェズサントス

1
TDDを使用してこの製品を構築することを明記するために、投稿に明確性を追加しました。そのため、問題の統合テストに裏付けられた数千のユニットテストが既にあります。。
ジェズサントス

8

統合テストは実際のユーザーを模倣する必要があるため、常に長時間実行されます。このため、すべてを同時に実行しないでください!

あなたはすでにクラウドで何かを実行していることを考えると、あなたは複数のマシンでテストをスケーリングするための主要な位置にいるように思えます。

極端な場合、テストごとに1つの新しい環境を起動し、それらをすべて同時に実行します。統合テストは、実行時間の最も長いテストの場合にのみかかります。


良いアイデア!そのような戦略を見て、いくつかのツールでヘルプがテストに分散することを
Jezzサントス

4

テストの削減/最適化は私にとって最良のアイデアのように思えますが、それが選択肢ではない場合、提案する代替手段があります(ただし、いくつかの単純な専用ツールを構築する必要があります)。

同様の問題に直面しましたが、統合テストでは発生しませんでした(数分で実行されました)。代わりに、単にビルド内にありました。大規模なCコードベースは、ビルドに数時間かかります。

非常に無駄だと思ったのは、ほんの数個のソースファイルが変更されたとしても、全体を一から再構築するという事実(約20,000個のソースファイル/コンパイル単位)であり、数秒または数分しかかからない変更に何時間も費やしたという事実です最悪の場合。

そこで、ビルドサーバーでインクリメンタルリンクを試みましたが、それは信頼できませんでした。それは時々偽陰性を与え、いくつかのコミットでビルドに失敗し、その後完全な再構築で成功します。さらに悪いことに、開発者が壊れたビルドをメインブランチにマージする場合にのみ、誤検知が発生してビルドの成功を報告することがありました。そのため、開発者がプライベートブランチから変更をプッシュするたびに、すべてを再構築することに戻りました。

私はこれがとても嫌いでした。開発者の半数がビデオゲームをプレイしているのに、ビルドを待っている間に他にやることがほとんどないという理由だけで、私は会議室に入りました。私はビルドを待っている間にコードで作業できるようにコミットしたらマルチタスクと新しいブランチを開始して生産性を獲得しようとしましたが、テストまたはビルドが失敗すると、その時点以降の変更をキューイングするのが大変になりましたそしてすべてを修正し、すべてを縫い戻そうとします。

待機中のサイドプロジェクト、後で統合

そのため、代わりに私がやったのは、アプリケーションの骨格フレームワークを作成することでした。同じ種類の基本的なUIと、SDKの関連部分を作成して、個別のプロジェクトとして開発することでした。次に、メインプロジェクトの外部でビルドを待っている間に、それに対して独立したコードを記述します。少なくとも生産性を維持できるようにするためのコーディングが少なくとも必要になり、その後、製品の外部で行われた作業をプロジェクトのサイドスニペットに統合し始めました。開発者が待ち構えている場合、それは開発者にとっての1つの戦略です。

ソースファイルを手動で解析して、再構築/再実行する対象を把握する

それでも私は、すべてを常に再構築するのに時間を浪費していることを嫌っていました。そこで、実際にファイルをスキャンして変更を確認し、関連するプロジェクトのみを再構築するコードを作成するために、数週間にわたって自分自身にそれを引き受けました-それでも完全な再構築、増分リンクはなく、再構築が必要なプロジェクトのみ(依存ファイルが再帰的に解析され、変更されました)。それは完全に信頼でき、徹底的にデモンストレーションとテストを行った後、そのソリューションを使用することができました。必要なプロジェクトのみを再構築するため、平均ビルド時間は数時間から数分に短縮されました(ただし、SDKの中央の変更には1時間かかる場合がありますが、ローカライズされた変更よりもはるかに少ない頻度で行いました)。

同じ戦略が統合テストに適用できるはずです。ソースファイルを再帰的に解析して、統合テストがどのファイルに依存しているかを調べます(例:importJava、#includeCまたはC ++で)サーバー側で、およびそれらのファイルからインクルード/インポートされたファイルなどで、システムの完全なインクルード/インポート依存ファイルグラフを構築します。DAGを形成するビルド解析とは異なり、グラフは、間接的に実行できるコードを含む変更されたファイルに関心があるため、無向にする必要があります*。対象の統合テストのグラフ内のファイルのいずれかが変更された場合にのみ、統合テストを再実行します。数百万行のコードであっても、この解析は1分もかからずに簡単に行えました。コンテンツファイルなど、統合テストに影響する可能性のあるソースコード以外のファイルがある場合は、おそらく統合テストの依存関係を示すソースコードのコメントにメタデータを書き込むことができます。再実行します。

*例として、test.cにfoo.cが含まれているfoo.hが含まれている場合、test.c、foo.h、またはfoo.cのいずれかを変更すると、統合テストに再実行が必要であるとマークされます。

特に正式な環境では、プログラムとテストに1日か2日かかる場合がありますが、統合テストでも動作するはずです。ビルドの時間範囲で待つ以外に選択肢がない場合は価値があると思います。終了する(構築、テスト、またはパッケージングプロセスなど)それは、この種の独自のソリューションを構築するのにかかる時間を小さくするだけでなく、チームのエネルギーを殺し、より大きなマージでの競合によって引き起こされるストレスを増やすだけで、ほんの数ヶ月で失われる非常に多くの工数に変換できます多くの場合、待機時間が無駄になります。チームが物事を待っている時間の大部分を費やしているのは、チーム全体にとって悪いことです。すべての小さな変更ごとに再構築/再実行/再パッケージ化されます。


3

統合テストが多すぎるように思えます。テストピラミッドを思い出してください。統合テストは中間に属します。

一例としての方法を用いてリポジトリをとりset(key,object)get(key)。このリポジトリは、コードベース全体で広く使用されています。このリポジトリに依存するすべてのメソッドは、偽のリポジトリでテストされます。これで、セット用と取得用の2つの統合テストのみが必要になります。

これらの統合テストの一部は、おそらく単体テストに変換できます。たとえば、私のビューのエンドツーエンドテストでは、正しい接続文字列と正しいドメインでサイトが正しく構成されていることのみをテストする必要があります。

統合テストでは、ORM、リポジトリ、およびキューの抽象化が正しいことをテストする必要があります。経験則として、統合テストにはドメインコードは必要ありません-抽象化のみです。

他のほとんどすべては、依存関係のスタブ化/モック化/偽造/メモリ内実装で単体テストできます。


1
興味深い視点。統合テストは、すべてのReST呼び出しのすべてのパラメーターのすべての順列を検証しようとはしていません。私たちの見解では、それは統合テストではありません。APIを介して重要なエンドツーエンドのシナリオを実行し、さまざまなバックエンドストアや他のシステムにヒットします。目的は、APIの変更に応じて、注意が必要なシナリオを特定する(つまり、期待どおりに動作しなくなる)ようにすることです。
ジェズサントス

1
アーキテクチャのさまざまなレベルで統合テストを実施しています。あなたの例では、データストアにアクセスするクラスのユニットテストがあり、データストアへの適切な呼び出しを行うことがわかります。ストアのコピーをセットアップし、データを正しく読み書きすることをテストする統合テストがあります。店で。次に、ユニットテストで作成したREST APIでこれらのデータクラスを使用します。次に、Webサービスを起動してコールスルーする統合テストを使用して、データが前後に行き来することを確認します。ここでテストが多すぎることを提案していますか?
ジェズサントス

あなたのコメントへの回答として回答を更新しました。
エスベンスコフペダーセン

2

継続的デリバリーパイプラインが一般的であるアジャイルまたはDevOps環境での私の経験では、各モジュールが完了または調整されるときに統合テストを実行する必要があります。たとえば、多くの連続配信パイプライン環境では、開発者ごとに1日に複数のコードを展開することは珍しくありません。この種の環境では、展開前の各開発フェーズの最後に統合テストのクイックセットを実行することが標準的な方法です。追加情報については、この主題に関する読書に含めるべき優れた電子書籍は、Katrina Clokieが執筆したDevOpsでのテストの実践ガイドです。

この方法で効率的にテストするには、専用のテスト環境で既存の完成したモジュールに対して、またはスタブとドライバーに対して、新しいコンポーネントをテストする必要があります。ニーズに応じて、各アプリケーションモジュールのスタブとドライバーのライブラリをフォルダーまたはライブラリに保存して、統合テストをすばやく繰り返し使用できるようにすることをお勧めします。スタブとドライバーをこのように整理すると、変更を繰り返し実行しやすくなり、更新を維持し、継続的なテストニーズに合わせて最適に実行できます。

考慮すべきもう1つのオプションは、元々2002年頃に開発されたサービス仮想化と呼ばれるソリューションです。これにより、仮想環境が作成され、複雑なエンタープライズDevOpsまたはアジャイル環境でのテスト目的で既存のリソースとモジュールの相互作用がシミュレートされます。

この記事は、企業で統合テストを行う方法について詳しく理解するのに役立ちます。


これは機能しますが(システムをこのようなモジュールに分割できる場合でも、すべての製品が分割できるわけではない場合)-かつては標準でしたが、統合を事実上遅らせているため、CI / CDのすべての利点が失われます。ちょっと反機敏だと思いませんか?このような統合テストで発見された問題は、特定のコミットに簡単かつ迅速に一致させることができないため、本番から発生するバグのように、最初から完全に調査する必要があります(修正するのにどれほど費用がかかるかはご存知でしょう)。
ダンコルニレスク

1

各テストを測定して、時間がかかっている場所を確認しましたか?そして、特に遅いビットがある場合は、コードベースのパフォーマンスを測定しました。全体的な問題は、テストまたは展開のいずれか、またはその両方ですか?

通常、比較的小さな変更での実行を最小限に抑えるために、統合テストの影響を軽減します。その後、ブランチが次のレベルに昇格したときに実行する「QA」実行の完全なテストを終了できます。そのため、devブランチの単体テストがあり、マージ時に縮小統合テストを実行し、リリース候補ブランチにマージするときに完全な統合テストを実行します。

したがって、コミットごとにすべてを再構築および再パッケージ化して再デプロイする必要はありません。開発環境でセットアップを整理して、可能な限り安価で可能な限り信頼できる展開を実行できます。VM全体を起動し、製品全体を展開する代わりに、VMを古いバージョンのままにして、たとえば新しいバイナリを所定の場所にコピーします(必要に応じてYMMV)。

この全体的な楽観的アプローチでは、まだ完全なテストが必要ですが、後の段階で時間がかからないときに実行できます。(たとえば、午前中に開発者が問題を解決できる問題がある場合は、夜間に完全なテストを1回実行できます)。これには、翌日のテストのために統合リグで製品を更新するという利点もあります。開発者が物事を変更すると、1日だけ更新されなくなる可能性があります。

セキュリティベースの静的分析ツールを実行する際にも同様の問題が発生しました。完全な実行には時間がかかるため、開発者のコ​​ミットから統合コミットに実行を移しました(つまり、devが終了したと言ったシステムがあり、perfを含むより多くのテストが実行された「レベル2」ブランチにマージされました)テストが完了したら、展開のためにQAブランチにマージされました。アイデアは、夜間に行われる実行に継続的に発生する通常の実行を削除することです。開発者は午前中に結果を取得し、開発に影響しません開発サイクルの後半まで集中してください)。


1

ある時点で、統合テストの完全なセットは、高価なハードウェアであっても、完了するのに何時間もかかる場合があります。オプションの1つは、これらのテストの大部分をすべてのコミットで実行するのではなく、代わりに毎晩、または連続バッチモード(複数のコミットごとに1回)で実行することです。

ただし、これにより新しい問題が発生します。開発者はすぐにフィードバックを受けず、壊れたビルドは気付かれない場合があります。これを修正するには、何かが常に壊れていることを知ることが重要です。次のような通知ツールを作成するCatlightTeamCityのトレイ通知機能非常に便利です。

しかし、さらに別の問題があります。開発者がビルドが壊れていることに気付いても、急いでチェックすることはありません。結局のところ、他の誰かが既にそれをチェックしているかもしれませんよね?

そのため、これらの2つのツールには「ビルド調査」機能があります。開発チームの誰かが実際に壊れたビルドをチェックして修正しているかどうかがわかります。開発者は、ビルドの確認を自発的に行うことができ、それが起こるまで、チームの全員が時計の近くの赤いアイコンに悩まされます。


0

コードベースが大きくなっているように思えますが、コード管理がいくらか役立つでしょう。私たちはJavaを使用しているため、これを前提とする場合は事前に謝罪します。

  • 大規模なプロジェクトは、ライブラリにコンパイルする個々の小さなプロジェクトに分割する必要があります。nexusのようなJavaツールはこれを簡単にします。
  • すべてのライブラリはインターフェースを実装する必要があります。これは、高レベルのテストでライブラリをスタブアウトするのに役立ちます。これは、ライブラリがデータベースまたは外部データストア(メインフレームなど)にアクセスする場合に特に役立ちます。このような場合、メインフレームまたはデータベースのデータを繰り返し可能な状態にすることは、おそらく遅くなり、不可能になる可能性があります。
  • 各ライブラリの統合テストは包括的ですが、新しいライブラリソースがコミットされたときにのみ実行する必要があります。
  • 高レベルの統合テストでは、ライブラリを呼び出して、それらが完全であると仮定する必要があります。

私が働いているJavaショップはこのアプローチを使用しており、統合テストの実行を待つことはめったにありません。


感謝しますが、このコンテキストでの統合テストの目的と適用について、私たちは同じ理解をしていないと思います。統合テストと単体テストを混同している可能性があります。
ジェズサントス

0

CIパイプライン統合テスト(またはビルドを含むあらゆる種類の検証)を実行時間を長くしたり、限られたおよび/または高価なリソースを必要とする別の可能なアプローチは、コミット後の検証に基づいて従来のCIシステムから切り替えることです(これは輻輳の影響を受けやすい)コミット前の検証に基づいたもの。

変更をブランチに直接コミットする代わりに、開発者は、検証を実行する中央自動化された検証システムにそれらを送信します。

  • 成功した場合、自動的に変更をブランチにコミットします
  • 失敗した場合は、それぞれの提出者に変更を再評価するよう通知します

このようなアプローチにより、複数の送信された変更を組み合わせてテストすることができ、潜在的なCI検証の速度が何度も向上する可能性があります。

そのような例の1つは、OpenStackで使用されるGerrit / Zuulベースのゲーティングシステムです。

もう1つはApartCIです免責事項 -私はその作成者であり、それを提供する会社の創始者です)。

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