2022年8月12日
Continuous Integration
この記事では、HarnessでBazelとSonarqubeを併用してコードカバレッジを行う方法と、あなたにもできる方法について説明します。
ソフトウェア開発は光の速さで行われます。毎日何千ものコード行がコードベースに追加されていますが、これらのコード行がアプリケーションの一部として実行されているのか、それともただソースコードの中に眠っているのかを常に検証する必要があります。
新しいコードや変更されたコードが実行されているかどうかを確認するために、開発者はユニットテストを書きます。ユニットテストは、メインリポジトリの一部になる前に、新しいコードや変更されたコードが期待通りに動作しているか、合格・不合格の両方の条件で確認するものです。
Bazelは、Googleが開発したビルドツールです。Googleはこのツールを社内で利用して おり、Blazeと呼ばれ、アプリケーションのビルドに利用されています。オープンソースの最新ビルドツールであり、現在多くの開発が行われている。しかし、Bazelの最大の特徴はインクリメンタル・ビルドで、Bazelはソースコードのどこが変更されたかを識別し、その部分のみをビルドします - 残りの部分はキャッシュに残し、そこから読み込むだけです。つまり、最初のビルドには時間がかかりますが、その後、キャッシュを削除しない限り、インクリメンタル・ビルドのたびに、はるかに短い時間でビルドできるようになるのです。
Bazelの使い方の詳細については、こちらをご覧ください。
冒頭で述べたように、毎日数千行のコードがコードベースに追加されています。すべてのコードがアプリケーションの一部として実行されていることを確認することは、非常に重要です。理想を言えば、ソースコードの中に使われずに眠っているコードが1行もないことが望ましいのですが、それは良いコーディングのやり方とは言えません。
コードカバレッジとは、ソフトウェア業界で使われる用語で、合格と不合格の状況で、どれだけのコードが実行され到達可能か、そして、どんな状況でも到達不可能なコードがどれだけの量あるかを示すものです。さらに、テストの有効性を明らかにするものでもあります。この値は、ユニットテストなどのテストケースによって生成することができます。テストケースの実行によってカバーされた行が、カバレッジ率を生成します。ほとんどのソフトウェア会社は、コードカバレッジに80%以上の閾値を設けています。しかし、セキュリティや金融などの企業では、このカバレッジ率が100%になることもあります。
このパーセンテージが何を意味するのか、例を見てみましょう。ソース・ファイルに10行のコードがあり、コード・カバレッジの最低値が80%だとします。これは、あなたが追加・変更した10行のコードのうち、テストケースで到達可能なのは8行だけで、2行はどんな条件でも到達不可能であることを意味します。
コードカバレッジを計算することで、ソースコードの問題点とその改善方法を知ることができるからです。大規模なプロジェクトでは、コード・カバレッジが70~80%以上になると、アプリケーションのコード品質が維持されていることを意味します。
1. カバレッジの洞察。コードベース内にあるコードの何パーセントが実際に到達可能かを明らかにし、到達不可能なコードを特定し、それに対して必要なアクションを取ることができます。
2. 脆弱性。到達できな い余分なコードは、いつでも、誰でも、どのような方法でも悪用される可能性があります。ですから、不必要にコードベースの一部となったコードを特定したら、それを削除してアプリケーションを安全にすることができるのです。
3. テストケース。カバレッジの低下は、余分なコードだけが原因ではありません。しかし、テストケースの不足から来る場合もあります。つまり、開発者は、カバーされていない行をカバーするために、より多くのテストケースを特定し、書くことで、コードカバレッジ数を定義された閾値に近づけることができるのです。さらに、このことは、コードが様々な状況下でどのように機能するかについての確信につながります。
4. コードの品質。コードカバレッジを上げると、問題を発見しやすくなります。その結果、チームのテストに自信を持つことができ、コードの品質が向上します。
これらは、コードカバレッジの確保を検討する理由の一部に過ぎません。他にも重要な理由がたくさんあります。
コードカバレッジレポートを生成するツールは、市販されている有名なものが多くあります。Sonarqubeは業界で最も有名なツールの1つであり、Harnessもこれを使用しています。Sonarqubeは、コードカバレッジだけでなく、コードの重複、コードスメル、リスクレベル付きバグリストなど、複数の種類のレポートを生成することができます。以下は、Sonarサーバーのレポートテーブルのスクリーンショットです。
残念ながら、今のところBazelとSonarqubeを直接統合することはできません。そこでHarnessでは、サードパーティのコード(というよりパーサー)を使って、BazelとSonarqubeを統合しています。
Bazelは独自のアルゴリズムで、ソースコ ードに対してテストケースを実行し、カバレッジ数を生成します。その際、BazelはカバレッジレポートをLCOV形式で生成しますが、これはSonarqubeの形式と同じではありません。そこで、パーサーを使って、LCOV形式のファイルをSonarqubeが理解できる形式に変換しています。そして、そのファイルをSonarqubeのサーバーにアップロードして、メトリクス形式のテーブルで様々なレポートを表示させています。
現状では、BazelとSonarqubeによるコードカバレッジは、高価で時間のかかる作業です。そこでHarnessでは、GitHubのプルリクエストではなく、一定期間ごとにカバレッジの数値を実行し、機能所有者にレポートを送信しています。
Bazelは独自のアルゴリズムを持っており、"bazel coverage <Module Name>"というコマンドでLCOV形式のカバレッジ数を生成することができます。つまり、Bazelのファイルには何も余計なことを書く必要がありません。しかし、Sonarqubeと統合するために、いくつかのBazelターゲットをBazelファイルに定義する必要があります。1つのターゲットは全てのパース処理を行い、Sonarqubeが理解できるファイルを作成し、もう1つはSonarqubeサーバーにレポートを送信します。以下のステップは、この統合を実現するためのハイレベルな説明です。
1. コードベース内 に、先ほどのサードパーティのスニペットを格納するためのフォルダを作成します。Bazelはこれらのソースファイルから実行可能なターゲットを作成し、統合を完了します。
2. モジュールレベルでBazelターゲットを作成します。Monorepoに複数のモジュールがホストされていることを想定しています。このターゲットは、ソースファイル、テストケース、モジュール名に関する情報を取得します。
sq_project(
name = "sq_mycomponent",
project_key = "com.example.project:component",
project_name = "My Project :: Component",
srcs = [
"//path/to/component:java_srcs",
],
targets = [
"//path/to/component:component",
],
test_srcs = ["//path/to/component:java_test_srcs"],
test_targets = [
"//path/to/component:FirstComponentTest",
"//path/to/component:SecondComponentTest",
],
test_reports = ["//:test_reports"],
tags = ["manual"],
visibility = ["//visibility:public"],
)
3. Sonarサーバーのプロジェクト名と、そのプロジェクト名の一部としてモジュールのリストを含むBazelターゲットを、プロジェクトのルートレベルに作成します。
sonarqube(
name = "TargetName",
srcs = [],
coverage_report = ":coverage_report",
modules = get_sonarqube_targets_seperated(),
project_key = "portal project key",
project_name = "portal project name",
scm_info = [":git"],
sq_properties_filename = "sany-filename.properties",
tags = ["manual"],
targets = [],
)
4. 最後に、SonarサーバーのDNS名またはIPを指定して「bazel run <TargetName> -Dsonar.host.url=${SONAR_HOST_URL} -Dsonar.login=${SONAR_AUTH_TOKEN} 」を実行し、認証トークンと一緒にSonarサーバーにレポートを送ります。
顧客にソフトウェアを提供するには、多くの異なるコンポーネントが必要ですが、それらはすべて、成功した高品質の製品という同じ目標を共有しています。コード・カバレッジだけでは、コードの品質を決定するための指標とは言えません。しかし、高品質のコードを実現するためには、コード・カバレッジは重要な要素であると考えるべきです。
コード・カバレッジを日常的に考慮することで、QAエンジニアだけでなく、テストケースの観点から開発者もコードを認識することができます。さらに、コードカバレッジは、脆弱性を取り除き、余分なコードや不要な行を排除してソースコードをきれいに保つのに役立ちます。
このまま読み進めたいですか?MavenからBazelに乗り換えた理由については、別の記事でご紹介しています。
この記事はHarness社のウェブサイトで公開されているものをDigital Stacksが日本語に訳したものです。無断複製を禁じます。原文はこちらです。