web-dev-qa-db-ja.com

バージョン管理フックで単体テストを実行することは良い習慣ですか?

技術的な観点からは、特定のコミットをリモートのデフォルトブランチにマージする前に、単体テストを実行するいくつかのpre/post Pushフックを追加することが可能です。

私の質問は-ビルドテストでユニットテストを維持する方が良い(つまり、壊れたコミットをリポジトリに導入する)のか、それとも「悪い」コミットが発生しないようにするのが良いのかです。

私はこの2つのオプションに制限されていないことを理解しています。たとえば、マージコミットをリポジトリにプッシュする前に、すべてのコミットを分岐させてテストすることができます。しかし、この2つのソリューションを正確に選択する必要がある場合、どちらを選択し、どのような理由で正確に選択しますか?

44
shabunc

いいえ、そうではありません。2つの理由があります。

速度

コミットは高速でなければなりません。たとえば、500ミリ秒かかるコミットは遅すぎて、開発者がより慎重にコミットするように促します。 Hello Worldよりも大きいプロジェクトでは、数十または数百のテストが行​​われるため、事前コミット中にそれらを実行するには時間がかかります。

もちろん、分散型アーキテクチャで数分間、または単一のマシンで数週間または数か月にわたって実行される何千ものテストがある大規模なプロジェクトでは、状況はさらに悪化します。

最悪の部分は、それを速くするためにあなたができることは多くないということです。小規模なPythonプロジェクトで、たとえば100の単体テストがある場合、平均的なサーバーでの実行には少なくとも1秒かかりますが、多くの場合はそれよりもはるかに長くなります。 C#アプリケーションの場合、コンパイル時間のため、平均で4〜5秒です。

その時点から、時間を大幅に短縮するより優れたサーバーに追加の$ 10000を支払うか、または複数のサーバーでテストを実行して速度を落とすことができます。

数千のテスト(および機能テスト、システムテスト、統合テスト)があると、どちらもうまくいくので、数週間ではなく数分でテストを実行できますが、小規模なプロジェクトには役立ちません。

代わりに、次のことができます。

  • コミットを行う前に、ローカルで変更したコードに強く関連するテストを実行するよう開発者に勧めます。彼らはおそらく何千ものユニットテストを実行することはできませんが、それらの5-10を実行することはできます。

    関連するテストを見つけて実行することが実際に簡単(かつ高速)であることを確認してください。たとえば、Visual Studioは、最後の実行以降に行われた変更によって影響を受ける可能性のあるテストを検出できます。他のIDE /プラットフォーム/言語/フレームワークも同様の機能を備えている場合があります。

  • コミットを可能な限り高速に保ちます。多くの場合、スタイルルールの適用はそれを行う唯一の場所であり、そのようなチェックはしばしば驚くほど高速であるため、スタイルルールの適用はOKです。静的分析を行うことは、それが高速であるとすぐに問題ありません。単体テストを実行することはできません。

  • 継続的インテグレーションサーバーでユニットテストを実行します。

  • 開発者がビルドを壊したとき(またはユニットテストが失敗したとき)が自動的に通知されるようにしてください。これは、コンパイラーをコードに持ち込む可能性のある誤りのいくつかをチェックするツールと見なす場合、これは事実上同じです)。

    たとえば、最後のビルドを確認するためにWebページにアクセスすることは、解決策ではありません。それらは自動的に通知されます。ポップアップの表示またはSMSの送信は、通知方法の2つの例です。

  • 開発者がビルドを壊す(またはリグレッションテストに失敗する)ことは問題ではなく、発生したらすぐにそれを修正することが最優先事項であることを開発者が理解していることを確認してください。上司から明日出荷を依頼された優先度の高い機能に取り組んでいるかどうかは関係ありません。ビルドに失敗したため、修正する必要があります。

安全保障

リポジトリをホストするサーバーは、特にセキュリティ上の理由から、単体テストなどのカスタムコードを実行しないでください。これらの理由はすでに説明されています GitLabの同じサーバー上のCIランナー?

一方、ビルドサーバーでプロセスをpre-commitフックから起動することが考えられている場合は、コミットがさらに遅くなります。

35

私は仲間の回答者と意見を異にする人になりましょう。

これは、TFSの世界ではゲートチェックインとして知られています。ゲートチェックインを使用してブランチにチェックインしようとすると、シェルブセットがサーバーに送信されます。これにより、変更がビルドされ、指定された(読み取り:すべて)ユニットテストに合格します。そうでない場合は、ビルドを壊したのはあなたが悪いサルだと通知します。もしそうなら、変更はソース管理に入ります(そうです!)。

私の経験では、ゲートチェックインは、単体テストを成功させるための最も重要なプロセスの1つであり、ひいてはソフトウェアの品質です。

どうして?

  • ゲートチェックインは、壊れたテストを修正するように人々に強制するからです。壊れたテストがmustではなくcanになるとすぐに、怠惰なエンジニアや強引なビジネスパーソンが優先順位を下げます。
    • テストが長く続くほど、修正は難しくなります(そしてコストがかかります)。
  • mustテストを実行するのではなくshouldテストを実行すると同時に、テストの実行は怠惰な/忘れっぽいエンジニアや強引なビジネスマンによって回避されます。
  • ユニットテストがコミット時間に影響を与えるとすぐに、人々reallyはテストの作成に関心を持ち始めますnitテスト。スピードが重要です。再現性が重要です。信頼性が重要です。分離が重要です。

そしてもちろん、もともと持っていたメリットもあります。ゲートインチェックインと確実な一連のテストを行うと、すべてのチェンジセットが「安定」します。 「最後の適切なビルドはいつでしたか?」というオーバーヘッド(およびエラーの可能性)をすべて節約できます。 -すべてのビルドは開発に十分対応できます。

はい、テストをビルドして実行するには時間がかかります。私の経験では、適切なサイズのC#アプリと約5kの単体テストで5〜10分です。そして、私はそれを気にしません。はい、頻繁にチェックインする必要があります。ただし、タスクを頻繁に更新したり、メールをチェックしたり、コーヒーエンジニアや、ソフトウェアエンジニアがその時間を費やす仕事を構成する他の「コードに取り組んでいない」ものを取得したりする必要もあります。不良コードのチェックアウトは、5〜10分よりはるかにコストがかかります。

41
Telastyn

コミットは高速に実行する必要があります。コードをコミットするときに、サーバーにプッシュする必要があります。一連のテストを実行している間、数分待つ必要はありません。私はサーバーにプッシュすることに対して責任があり、コミットフックで私をベビーシッターする誰も必要としません。

つまり、サーバーに到達したら、すぐに(または短い時間枠内で)分析、単体テスト、および構築する必要があります。これは、単体テストが壊れている、コンパイルできない、または静的分析ツールで表示される混乱を利用できるようにしたことを警告します。これが速く行われると(ビルドと分析)、私のフィードバックが速くなり、修正が速くなります(思考が私の脳から完全に入れ替わっていません)。

したがって、クライアントのコミットフックにテストなどを配置しないでください。必要な場合は、サーバーにコミット後(CIサーバーがないため)またはCIビルドサーバーに配置し、コードの問題について適切に警告してください。ただし、そもそもコミットの発生をブロックしないでください。

また、テスト駆動開発の一部の解釈では、firstを壊すユニットテストをチェックインする必要がある必要がある。これにより、バグが存在することが示され、文書化されます。その後のチェックインは、ユニットテストをfixesするコードになります。単体テストに合格するまでチェックインを行わないと、問題の文書化に失敗した単体テストでのチェックの効果的な価値が低下します。

関連: 既知の欠陥の単体テストが必要ですか? および 失敗した単体テストでのチェックの値は何ですか?

40
user40980

原則として、ビルドを壊すようなメインラインへの変更を人々が行わないようにすることは理にかなっていると思います。つまり、リポジトリのメインブランチに変更を加えるプロセスでは、すべてのテストに合格する必要があります。ビルドを壊すことは、プロジェクトのすべてのエンジニアが他のことをするのに時間を浪費するという点で、コストがかかりすぎます。

ただし、コミットフックの特定のソリューションは適切な計画ではありません。

  1. 開発者は、コミットしながらテストが実行されるのを待つ必要があります。開発者がすべてのテストに合格するまで自分のワークステーションで待機する必要がある場合、貴重なエンジニアの時間を無駄にしていることになります。エンジニアは、テストが失敗したために元に戻す必要がある場合でも、次のタスクに進むことができる必要があります。
  2. 開発者は、ブランチで壊れたコードをコミットしたい場合があります。より大きなタスクでは、開発者バージョンのコードは、合格状態ではない多くの時間を費やす可能性があります。明らかに、そのコードをメインラインにマージすることは非常に悪いでしょう。しかし、開発者が自分の進行状況を追跡するためにバージョン管理を引き続き使用できることがかなり重要です。
  3. プロセスをスキップしてテストをバイパスするのには、たまに良い理由があります。
10
Winston Ewert

いいえ、他の回答が指摘したように、あなたはそうすべきではありません。

失敗するテストがないことが保証されているコードベースが必要な場合は、代わりに機能ブランチで開発し、プルリクエストをマスターに送信できます。次に、それらのプルリクエストを受け入れるための前提条件を定義できます。利点は、プッシュを非常に迅速に実行でき、テストがバックグラウンドで実行されることです。

3
Yogu

Mainブランチへのコミットごとにビルドとテストが成功するのを待たなければならないのは本当に恐ろしいことですが、誰もがこれに同意していると思います。

しかし、一貫したメインブランチを実現する方法は他にもあります。ここに1つの提案があります。TFSのゲートチェックインの流れに少し似ていますが、ブランチを持つすべてのバージョン管理システムに一般化できますが、主にgit用語を使用します。

  • 開発ブランチとメインブランチの間でのみマージをコミットできるステージングブランチがある

  • ビルドを開始またはキューに入れるフックをセットアップし、ステージングブランチで行われたコミットをテストしますが、コミッターを待機させません

  • ビルドとテストが成功すると、メインブランチが自動的にforwardになります(最新の場合)。

    注:メインブランチに自動的にmergeを入れないでください。テストされたマージは、メインブランチの観点からのフォワードマージではない場合、コミットの間にメインブランチにマージされると失敗する可能性があるためです。

結果として:

  • 可能であれば自動的にメインブランチへの人間のコミットを禁止しますが、抜け穴がある場合、またはこれを実施することが技術的に実現可能でない場合は、公式プロセスの一部としても禁止します。

    少なくとも、それが基本ルールになったら、誰かが意図せずに、または悪意なしにそれをしないようにすることができます。あなたはそれをやろうとするべきではありません。

  • 次の中から選択する必要があります。

    • 以前のビルドでテストされていないマージが失敗した場合に実際に失敗するマージを行う単一のステージングブランチ

      少なくとも、どのマージが失敗し、誰かに修正してもらうことはできますが、その後のビルドとテストの結果を(バージョン管理システムによって)簡単に追跡することはできません。

      ファイルの注釈(または非難)を見ることができますが、ファイル(構成など)を変更すると、予期しない場所でエラーが発生することがあります。ただし、これはかなりまれなイベントです。

    • 複数のステージングブランチ。これにより、競合のない正常なマージがメインブランチに到達できるようになります。

      他のステージングブランチに非競合のマージが失敗した場合でも。トレーサビリティは少し優れています。少なくとも、ある合併が別の合併による影響のある変更を予期していなかった場合はそうです。しかし、これもまた、毎日または毎週心配する必要がないほどまれです。

      ほとんどの場合、競合しないマージを行うには、ステージングブランチを慎重に分割することが重要です。チームごと、レイヤーごと、またはコンポーネント/プロジェクト/システム/ソリューションごと(名前を付けても)。

      その間にメインブランチが別のマージに転送された場合は、再度マージする必要があります。うまくいけば、これは競合しないマージや非常に少ない競合の問題ではありません。

ゲートチェックインと比較した場合の利点は、メインブランチは前進のみが許可され、間にコミットされたものと変更を自動的にマージしないため、メインブランチが機能することが保証されることです。したがって、3番目のポイントは本質的な違いです。

2
acelent

コードを送信するためのゲートとして、「ユニットテストに合格」を好みます。ただし、それを機能させるには、いくつかのことが必要になります。

アーティファクトをキャッシュするビルドフレームワークが必要になります。

特定のアーティファクトを使用して、(成功した)テスト実行からのテストステータスをキャッシュするテストフレームワークが必要になります。

こうすることで、ユニットテストに合格したチェックインは迅速になり(ソースからチェックイン前にテストをチェックしたときに作成されたアーティファクトへのクロスチェック)、ユニットテストに失敗したものはブロックされ、開発者はコンパイルとテストのサイクルが長いため、コミットする前にビルドをチェックすることを忘れてしまうと、次回のチェックも忘れないようにしてください。

2
Vatine

壊れたコミットはtrunkで許可されるべきではありません。なぜなら、トランクが本番環境に入る可能性があるからです。したがって、trunkになる前に、彼らが渡す必要があるゲートウェイがあることを確認する必要があります。ただし、トランク上にない限り、壊れたコミットはリポジトリ内で完全に問題なく実行できます。

一方、変更をリポジトリにプッシュする前に開発者に待機/問題の修正を要求することには、いくつかの欠点があります。

いくつかの例:

  • TDDでは、機能の実装を開始する前に、新機能の失敗したテストをコミットしてプッシュするのが一般的です。
  • 失敗したテストをコミットしてプッシュすることでバグを報告する場合も同様です
  • 不完全なコードをプッシュすると、2人以上のユーザーが機能を並行して簡単に作業できるようになります
  • CIインフラストラクチャの検証には時間がかかることがありますが、開発者は待つ必要はありません
1
tkruse

それはプロジェクトと、「コミット」で実行される自動テストの範囲に依存すると思います。

チェックイントリガーで実行したいテストが本当に速い場合、または開発者のワークフローがそのようなチェックイン後に何らかの管理作業を強制する場合は、あまり問題にならず、開発者にチェックのみを強制する必要があります。最も基本的なテストを絶対に実行するものです。 (私は、そのようなトリガーで最も基本的なテストのみを実行することを想定しています。)

速度/ワークフローが許す限り、テストに失敗した変更を他の開発者にプッシュしないことは良いことだと思います。テストを実行した場合にのみ、失敗したかどうかがわかります。

あなたは質問に「commit ... to remote branch」と書いて、これは私にこれが意味することを意味します(a)開発者が数分ごとに行うことではないので、小さな待機は非常によく受け入れられます、そして(b)その後このようなコミットは、コードの変更が他の開発者に影響を与える可能性があるため、追加のチェックが必要になる場合があります。

そのような操作については、「待機中に開発者のつまみをいじらないでください」に関する他の回答にも同意できます。

1
Martin Ba