ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Java (JVM 言語) におけるコードカバレッジの計測方法

Java と Kotlin で書かれたアプリケーションのコードテストのカバレッジを取りたいなーと思って、コードカバレッジ計測ツールについて調べてみてる。

JVM 言語におけるコードカバレッジ計測の方法

JaCoCo のドキュメントにわかりやすくまとまっている。

Coverage information has to be collected at runtime. For this purpose JaCoCo creates instrumented versions of the original class definitions. The instrumentation process happens on-the-fly during class loading using so called Java agents.

There are several different approaches to collect coverage information. For each approach different implementation techniques are known. The following diagram gives an overview with the techniques used by JaCoCo highlighted:

f:id:nobuoka:20180228230512p:plain

JaCoCo - Implementation Design

カバレッジ情報は実行時に収集されるものなので、情報収集のための仕組みが必要。

大きく分けると Runtime Profiling と Instrumentation の 2 つの仕組みがある。 前者は JVM の仕組み (JVMTI や JVMPI) を使うもの。 後者は実行対象のプログラムの方に収集用の仕組みを搭載する (これを instrumentation というらしい?) もの。

Runtime Profiling

JVM TI は Java Virtual Machine Tool Interface の略。 Java SE 5 で導入された。

JVM TI は JVM 上で動くアプリケーションの状態を検査したり、実行を制御したりするためのプログラミングインターフェイスらしい。

JVMPI は JVM TI よりも古くからある同じようなインターフェイスらしい。 Java SE 5 で (JVM TI ができたことで) 非推奨になり、Java SE 6 で廃止された模様。

これらを使ったコードカバレッジは、アプリケーション側に何も手を入れなくて良いことが利点だと思われる。 が、詳細はわからない。

Instrumentation

上でも説明したように、計測対象のアプリケーションコードに情報収集のための仕組みを搭載する方法が、この Instrumentation である。 Runtime Profiling と異なり、Android アプリの実行環境のような非 JVM 環境でも使用できるという利点がありそう *1

上の図を見るとわかるように、様々な方法がある。

  • ソースコードを変更するもの。
  • バイトコードを変更するもの。
    • オフライン (offline) で変更するもの。 (= JVM に読み込まれる前の状態、例えばクラスファイルそのものを変更する。)
      • Replace と Inject って書かれてるけどそれぞれの意味はわからない。
    • オンザフライ (on-the-fly) で変更するもの。 (= JVM 読み込まれる際などにオンメモリで変更する。)
      • クラスローダで変更する方法と、Java Agent を用いる方法がある。

JaCoCo は、Java Agent を用いてオンザフライでバイトコードを変更する方式である。 *2

Java Agent について

Java Agent については java.lang.instrument パッケージの Javadoc に書かれている。

-javaagent:[=] というコマンドラインオプションで指定して使用できるものである。 次のページも参考になる。

エージェントの背景にある基本概念は、「JVM がクラスをロードする場合、エージェントはそのクラスのバイトコードを修正できる」 という考え方です。

それぞれの特徴

現時点で自分がわかっている範囲で特徴を書いておく。

  • ソースコードを変更する方式は、言語によって使用できるかどうかが変わる。 例えば Java 言語に対応しているツールでも Kotlin には対応していなかったりする。
    • 一方でバイトコードを変更する方式は、JVM 言語であればどれにでも対応できるはず。
  • オフラインで変更する方式は、ビルド時にクラスファイルが書き換えられてしまうので、それをそのまま本番アーティファクトのビルド時に使用できない。 (使用したらダメというわけではないが、パフォーマンスが落ちるなどの問題が起こる。)

様々なコードカバレッジツール

JVM 言語用の様々なコードカバレッジツールについて、Clover のブログで比較紹介してくれている。 (最終更新が 2017 年春なので情報はちょっと古いかもしれない。)

有名どころとしては JaCoCoOpenClover (Atlassian Clover がオープンソース化されたもの)、JCov といったところだと思う。

Instrumentation 方式に着目すると、JaCoCo と JCov はオフラインおよびオンザフライのバイトコード instrumentation に対応しており、Clover はソースファイル instrumentation に対応している。 なので JaCoCo や JCov は (JVM 言語なら何でも対応できるので) Kotlin にも対応するが、Clover は (対応言語に入っていない) Kotlin には対応しない。

Clover と JaCoCo を軽く使ってみたところ、設定の簡単さはどちらも同じ。 出力される HTML を見ると JaCoCo は単純な内容で、Clover の方はプロジェクトリスクの高いものを表示したり、視覚的だったりと、結果表示については高機能さを感じた。

今回は Kotlin でのカバレッジも取りたいので、(対応していない) Clover は選外で、JaCoCo か JCov のどっちかを使うことになりそう。

*1:憶測です

*2:オプションでオフラインでの instrumentation も可能。