見出し画像

CI/CD におけるテストの重要性と実行頻度について


はじめに

こんにちは、くるふとです。
ナビタイムジャパンでは、時刻表 API や地図描画 API の 開発・運用業務を主に担当しています。
今日のソフトウェア開発業務において、 CI/CD の重要性は非常に高くなっています。CI/CD の設計を考えるにあたって、テスト工程をどう組むかは議論の対象になりやすいです。
今回の記事では CI/CD におけるテストの重要性と実行頻度について考察していきます。

CI/CD とは

まず、CI/CD とは何かから整理していきます。今回は Continuous Delivery Foundation の定義を引用します。
Continuous Delivery Foundation はソフトウェア開発における CI/CD の促進を目的とした団体です。
CI/CDのベストプラクティスを共有し、業界標準を確立するために活動しています。また、 Jenkins をはじめとした、複数のオープンソースプロジェクトをサポートしています。

Continuous Delivery Foundation は Github リポジトリにて CI/CD の定義をまとめています。今回はそちらの内容を引用します。

CI/CD

CI/CD: A phrase with no clear origin which popped into existence around 2014, which combines Continuous Integration (CI) with either Continuous Delivery (CD) or Continuous Deployment. The phrase is used to refer to tools and practices which encompass one or more of: Continuous Integration, Continuous Delivery and Continuous Deployment.

CI/CD とは、継続的インテグレーション、継続的デリバリー、継続的デプロイメントを組み合わせたものです。
継続的インテグレーション、継続的デリバリー、継続的デプロイメントについても後の項目でそれぞれ説明いたします。

継続的インテグレーション

Continuous Integration (CI): The process of combining code changes frequently, where each change is verified on check in.

コード変更を頻繁に組み合わせる過程のことを指します。各変更はチェックイン(コミット)の際に検証されます。
例えば、リポジトリの静的解析や単体テストなどは各コミットごとにローカル、あるいは CI サーバー上で実行されます。そういった機構や手法を CI と呼称します。

継続的デリバリー

Continuous Delivery (CD): A software development practice in which teams release software changes to users safely, quickly and sustainably by:
  1. Proving that changes can be released at any time
  2. Automating release processes

チームがソフトウェアの変更を安全、迅速、持続的にユーザーにリリースするための開発プラクティスのことを指します。
上記のようなプラクティスは以下によって実現されます。

  • 変更をいつでもリリースできることの証明

  • リリースプロセスの自動化

継続的デプロイメント

Continuous Deployment: Working software is released to users automatically on every commit.

動作するソフトウェアは、コミットごとに自動的にユーザーにリリースされます。
継続的デプロイメントは継続的デリバリーの先にあるオプションのような立ち位置で、本番環境へのデプロイの完全自動化までを範囲としています。

CI/CD におけるテストの重要性

CI/CD においてテストは非常に重要な存在です。なぜでしょうか?
先の項目で述べた CI/CD の定義をベースに説明していきます。継続的デリバリーの定義を見直してみると、以下のような一文がありました。

1. Proving that changes can be released at any time

いつでも安全にソフトウェアの変更をリリースするため、ソフトウェアの品質を常に一定以上に保つ必要があります。品質を担保する手段として、テストが重要視されています。また、継続的なリリースを実現するために、それらのテストが自動化され、繰り返し実行可能であることも同時に求められています。

CI/CD におけるテストの種類

CI/CD において重要視されるテストですが、いくつかの種類に分けられます。すべての種類をあげることはできませんが、当社の時刻表 API を例に主要なテストをいくつか紹介いたします。

時刻表 API について

まず最初に、当社の時刻表 API について簡単に触れておきます。
鉄道、バスなどの公共交通機関の時刻表データを、当社では API を介して提供しています。
当社のサービスで確認できる時刻表画面は、この API が提供する値を元に展開されています。

テストの種類

当社の時刻表 API では以下のテストを実行しています。

  • 静的解析

    • Linter による警告がないか

    • 各関数の循環的複雑度が基準値以下となっているか

  • 単体テスト

    • 各関数の振る舞いが期待通りであるか

    • テストカバレッジが基準値以上となっているか

  • E2E テスト

    • API が期待通りの振る舞いをする(≒レスポンスを返却する)か

  • 回帰テスト

    • マージされた変更が API の既存機能の振る舞いに影響を与えていないか

  • 負荷試験

    • 本番環境とほぼ同等のシステム構成で期待通りの振る舞いをするか

これらのテストが、 API の変更をリリースするまでの流れで自動実行されます。

テストの自動実行における問題点

複数のテストを自動で実行するに当たって問題となってくるのが、各テストの実行頻度についてです。
例えば、単体テストや静的解析は、コミット単位で自動実行されるケースが多いです。しかし、結合テストや負荷試験のような、一定以上のサーバーリソースを要求するテストがコミット毎に実行されるとどうでしょうか?おそらく、実行されるテストのサーバーコストが想定よりも多くかかる結果となります。
では、コストを抑えるために、テストの実行頻度はより少ない方が理想なのでしょうか?
例えば、単体テストや静的解析も含めて日次での実行にしたとします。その場合、コミットに混入したバグを検知するには次の日を待つ必要が出てきます。こうなると、本来もっと短い時間で対応できたバグの修正に時間がかかってしまい、結果的にユーザーに価値を届ける(≒リリースする)までのリードタイムが伸びてしまいます。
このように、各テストの実行頻度の設定は非常に悩ましく、テストの種類や規模によって変えていかなければなりません。

テストのサイズと実行頻度

では、テストの実行頻度はどのように決定していけば良いでしょうか?
当社 API では、各テストのサイズを整理して、それに基づいて実行頻度を設定しています。
テストサイズとは Google が提唱し始めた考え方で、利用するリソース量や実行時間などによってテストの規模(≒サイズ)を定義していくような考え方です。

テストサイズを決定する基準

テストサイズを決定するものとして、時刻表 API では以下の基準を設けています。

  • ファイルシステムへのアクセス

  • 外部リソースの利用

    • データベース

    • S3 などのストレージ

    • 外部 API

    • クラウドベンダーのマネージドサービス

  • 実行環境

    • サーバー台数

    • API サーバー以外のインフラリソース

      • ロードバランサー

      • CDN

  • 実行時間

時刻表 API の開発・運用では外部リソースの有無とランニングコストが重視されています。
そのため、テストサイズを設定する際も上記の内容の比重が大きくなっています。
Google が提唱するテストサイズの基準と異なっていますが、当社のコンテキストを考慮しての結果です。

時刻表 API における各テストのサイズと実行頻度

上記の基準をベースに、時刻表 API の各テストのサイズと実行頻度を定義すると以下のような形になります。

  • 静的解析

    • ファイルシステムへのアクセス: ×

    • 外部リソースの利用: ×

    • 実行環境: ローカル、CI サーバー

    • 実行時間: 1~2 分程度

    • サイズ: 小

    • 実行頻度: コミット毎

  • 単体テスト

    • ファイルシステムへのアクセス: ×

    • 外部リソースの利用: ×

    • 実行環境: ローカル、CI サーバー

    • 実行時間: 1~2 分程度

    • サイズ: 小

    • 実行頻度: コミット毎

  • E2E テスト

    • ファイルシステムへのアクセス: ○ (起動時の設定ファイルなど)

    • 外部リソースの利用: ○(S3 、データベースなど)

    • 実行環境: 検証専用サーバー

    • 実行時間: 10 分程度

    • サイズ: 中

    • 実行頻度: 日次

  • 回帰テスト

    • ファイルシステムへのアクセス: ○ (起動時の設定ファイルなど)

    • 外部リソースの利用: ○(S3 、データベースなど)

    • 実行環境: 検証専用サーバー

    • 実行時間: 30 分程度

    • サイズ: 中~大

    • 実行頻度: 日次

  • 負荷試験

    • ファイルシステムへのアクセス: ○ (起動時の設定ファイルなど)

    • 外部リソースの利用: ○(S3 、データベースなど)

    • 実行環境: 本番同等のインフラ構成

    • 実行時間: 45~60 分程度

    • サイズ: 大

    • 実行頻度: リリース作業毎(週1回程度)

時刻表 API のテストフロー

時刻表 API は週1回リリース作業を実施しています。
静的解析や単体テストは高頻度で実行できるようにし、 E2E テストと回帰テストを日次で実行しています。そして、負荷試験は本番環境へのデプロイ作業の直前で実施するような工程にしています。
実は E2E テストと回帰テストも以前は週1回、負荷試験と同様に本番環境へのデプロイ作業前での実施でした。
しかし、 E2E テストと回帰テストで検知できる不具合が少なくなく、またサイズも負荷試験と比べると小さいという都合があったので、より高い頻度で実行できるよう日次での実行にシフトしていったという背景があります。
このような形で各テストのサイズと実行頻度を整理し、テスト工程のコストパフォーマンスを最適化しています。

さいごに

いかがでしたでしょうか?
CI/CD の設計は奥が深く、ソフトウェア開発の中でも非常に面白い分野の1つです。
当記事の内容をきっかけに、皆様の開発体験がより向上することを心から願っております。
CI/CD の神様のご加護がありますように。

参考書籍・参考資料