Featured image of post sbt-jacocoでJUnitのカバレッジレポートをHTML出力する方法

sbt-jacocoでJUnitのカバレッジレポートをHTML出力する方法

PlayFramework2.7のプロジェクトでsbt-jacocoプラグインを使い、JUnitのカバレッジレポートをHTML形式で自動生成する方法。plugins.sbtへの追記とsbt jacocoでの実行、分岐を網羅していない場合の見方まで♪

1458文字

ユニットテストのガバレッジを自動生成しよう

皆さんはユニットテスト書いていますか?

品質をあげるにはしっかりとユニットテストを書いて、CI環境でしっかり実施する事が大事です。

今回ご紹介するのは、ユニットテストの実施によって実行されたソース部分のガバレッジレポートをHTML形式で自動生成してくれるJaCoCoというプラグインをご紹介しようと思います。

出力対象のパッケージを制御する方法については以下でご紹介していますので、合わせてみて頂ければなと思います。

関連記事 【ユニットテスト】JUnitのJaCoCoの出力対象パッケージを指定する方法【PlayFramework2.7系】 2020/03/04 534文字 約2分で読めます IT Java Mac Windows アプリケーション

手順

sbt-jacocoプラグインの追加

まずはプロジェクトにsbt用のJaCoCoプラグインを追加しましょう。

project/plugins.sbtに以下を追記してください。

project/plugins.sbt
// The Play plugin
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.0")

// Defines scaffolding (found under .g8 folder)
// http://www.foundweekends.org/giter8/scaffolding.html
// sbt "g8Scaffold form"
addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0")
// JaCoCo  ★追加
addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "3.2.0")  ★追加

設定はたったこれだけです!

実行方法

テスト実行時のコマンドをsbt jacocoに変えて実行する事でJUnitの実行とレポートの生成を行なってくれます。

[adsense-responsive]

sbt jacoco
...
()
...
[info] ------- Jacoco Coverage Report -------
[info]
[info] Lines: 58.49% (>= required 0.0%) covered, 44 of 106 missed, OK
[info] Instructions: 46.5% (>= required 0.0%) covered, 635 of 1187 missed, OK
[info] Branches: 22.92% (>= required 0.0%) covered, 37 of 48 missed, OK
[info] Methods: 33.02% (>= required 0.0%) covered, 71 of 106 missed, OK
[info] Complexity: 27.69% (>= required 0.0%) covered, 94 of 130 missed, OK
[info] Class: 73.33% (>= required 0.0%) covered, 4 of 15 missed, OK
[info]
[info] Check /Users/blogenist/sample/java-sample/target/scala-2.13/jacoco/report for detailed report
[info]
[success] Total time: 5 s, completed 2020/02/21 21:11:02

生成ディレクトリは `{プロジェクトルート}/target/scala-2.13/jacoco/report` です。

結果の中には、PlayFrameworkが自動で生成するパッケージ(routerviewscontroller.javascriptなどがありますがスルーしましょう。)

取り急ぎ、sbtが自動生成してくれたHomeControllerクラスのテスト結果を見てみましょう。

緑色になっているところがユニットテストで実行されたコード部分になります。

ユニットテスト作成の一つの指標として、全てのコードを通っていることを担保するには良いガバレッジに使えそうですね♪

実行されていない場合

実行されていない場合はこのように赤くなります。

なので、まずは全ての行が緑色になるようにユニットテストやソースの修正をしましょう。

※今回は紹介するためにテスト元クラスをいじってJaCoCoの動きをご紹介していますorz

if文を網羅していない場合

また、以下のような引数の値によってif文などの分岐が存在する場合に、ユニットテストで網羅できていない場合は以下のように黄色くなります。

[adsense-responsive]

app.controllers.HomeController.java
package controllers;

import play.mvc.*;

/**
 * This controller contains an action to handle HTTP requests
 * to the application's home page.
 */
public class HomeController extends Controller {

    /**
     * An action that renders an HTML page with a welcome message.
     * The configuration in the <code>routes</code> file means that
     * this method will be called when the application receives a
     * <code>GET</code> request with a path of <code>/</code>.
     */
    public Result index(Boolean isAvailable) {
      if( isAvailable ){
        hello();
      }
      return ok(views.html.index.render());
    }

    private void hello(){
      System.out.println("Hello World!!!");
    }

}

test.controllers.HomeControllerTest.java
package controllers;

import org.junit.Test;
import play.Application;
import play.inject.guice.GuiceApplicationBuilder;
import play.mvc.Http;
import play.mvc.Result;
import play.test.WithApplication;

import static org.junit.Assert.assertEquals;
import static play.mvc.Http.Status.OK;
import static play.test.Helpers.GET;
import static play.test.Helpers.route;

public class HomeControllerTest extends WithApplication {

    @Override
    protected Application provideApplication() {
        return new GuiceApplicationBuilder().build();
    }

    @Test
    public void testIndexByFalse() {
        Http.RequestBuilder request = new Http.RequestBuilder()
                .method(GET)
                .uri("/?isAvailable=false");

        Result result = route(app, request);
        assertEquals(OK, result.status());
    }

}

その場合は、ユニットテスト側で分岐を網羅するようなケースを追加してあげることでガバレッジが100%になります。

test.controllers.HomeControllerTest.java
package controllers;

import org.junit.Test;
import play.Application;
import play.inject.guice.GuiceApplicationBuilder;
import play.mvc.Http;
import play.mvc.Result;
import play.test.WithApplication;

import static org.junit.Assert.assertEquals;
import static play.mvc.Http.Status.OK;
import static play.test.Helpers.GET;
import static play.test.Helpers.route;

public class HomeControllerTest extends WithApplication {

    @Override
    protected Application provideApplication() {
        return new GuiceApplicationBuilder().build();
    }

    @Test
    public void testIndexByTrue() {
        Http.RequestBuilder request = new Http.RequestBuilder()
                .method(GET)
                .uri("/?isAvailable=true");

        Result result = route(app, request);
        assertEquals(OK, result.status());
    }

    @Test
    public void testIndexByFalse() {
        Http.RequestBuilder request = new Http.RequestBuilder()
                .method(GET)
                .uri("/?isAvailable=false");

        Result result = route(app, request);
        assertEquals(OK, result.status());
    }

}

ちなみに、分岐の中に入ってソースの全てが実行されたとしても、falseのパターンが実行されていないと判断されて黄色になりますのでご注意を。

終わりに

以上のように、ユニットテストと組み合わせる事でとても有能なガバレッジを自動生成してくれます。

ユニットテストの実施結果を残すことはとても大事なので、是非導入してみてください♪