エンジニアリング

へたれエンジニアがJaCoCoを使ってカバレッジレポートをとってみた(gradle×JaCoCo)

仕事でスマホアプリの開発をしているとこんな感じのチーム体制が良くあったりする

ディレクター

エンジニア

QA:Quality Assurance(テスター・品質管理)

基本的にエンジニアは動作確認をしてQAさんにテストを依頼する流れになるわけだが、ここで行えるテストは要件・仕様に対するテストとなるのでブラックボックス寄りのテストとなる。
その為Exceptionのエラーハンドリングなどホワイトボックス観点のテストは実施されないことが多い
その場合どうするかといえば・・・

  1. 技術的教育を行ないソースを読めるようにQAになってもらいテストをする
  2. コードカバレッジを測り未到達の部分のみエンジニアがテストする

上記の場合、1だと育成なども踏まえるのでだいぶ遠い未来になってしまう、どちらかというと今見える未来は2の方だと思い、カバレッジツールの「JaCoCo」をどんなもんか試してみた

JaCoCoとは・・・

・カバレッジツール

・C0カバレッジとC1の計測が可能

・HTML,XML,CSVでのレポート出力

・Maven gradleでシュッと動かせる

C0カバレッジ・・・・・命令網羅 C1カバレッジ・・・・・分岐網羅 C2カバレッジ・・・・・条件網羅

※C0:すべての命令のなかでテスト実行された命令比率だから処理a b cが実行されれば良いから hoge=”A” hoge=”C”の実施で100%

※C1:すべての判定条件のなかでテスト実行された判定条件の比率だからif2つなので 2^2 = 4でカバレッジ100%(実質3ケース)で100%

※C2:すべての条件を分岐として捉える or 条件のところを2つと考えるとCをあわせると3条件がある 2^3 = 8でカバレッジ100%(実質は言えないケースがあるので4ケース)

こんな感じのコードで検証すると分かりやすいね

String hoge = "A";

if(hoge.equals("A") || hoge.equals("B")) {
  System.out.println("処理a");

} else {
  System.out.println("処理b");
}

if(hoge.equals("C")) {
  System.out.println("処理c");
}

ローカルでの動かし方

gradleで適当にセットアップ

gradleの自動生成と、JaCoCo使いますよ宣言とレポート吐きますよ宣言を加える

build.gradle

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'jacoco'

sourceCompatibility = 1.8
version = '1.8'
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart',
'Implementation-Version': version
  }
}

repositories {
mavenCentral()
}

dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}

test {
systemProperties 'property': 'value'
}

jacocoTestReport {
additionalSourceDirs = files(sourceSets.main.allJava.srcDirs)
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/reports/jacoco"
sourceSets sourceSets.main
  }
}

uploadArchives {
repositories {
flatDir {
dirs 'repos'
    }
  }
}

テストコード

Person.java

public class Person {
  private final String name;

public Person(String name) {
  this.name = name;
  new GrowthList();
}

public String getName() {

  String hoge = name;

if(hoge.equals("A") || hoge.equals("B")) {
  System.out.println("処理a");

} else {
  System.out.println("処理b");
}

if(hoge.equals("C")) {
  System.out.println("処理c");
}

return name;
  }
}

PersonTest.java

public class PersonTest {
@Test
public void canConstructAPersonWithAName() {
  Person personA = new Person("A");
  Person personB = new Person("B");
  Person personC = new Person("C");
  assertEquals("A", personA.getName());
  assertEquals("B", personB.getName());
  assertEquals("C", personC.getName());
  }
}

実行結果を見てみる

まずはAだけ通してみる

PersonTest.java

public class PersonTest {
@Test
public void canConstructAPersonWithAName() {
Person personA = new Person("A");
  //Person personB = new Person("B");
  //Person personC = new Person("C");
  assertEquals("A", personA.getName());
  //assertEquals("B", personB.getName());
  //assertEquals("C", personC.getName());

  }
}
gradle test jacocoTestReport

で実行

スクリーンショット 2017-11-26 15.54.39 スクリーンショット 2017-11-26 15.54.47

通ったところがグリーンになりました!

続いてCを通してみる(C0:命令網羅)

PersonTest.java

public class PersonTest {
@Test
public void canConstructAPersonWithAName() {
  Person personA = new Person("A");
  //Person personB = new Person("B");
  Person personC = new Person("C");
  assertEquals("A", personA.getName());
  //assertEquals("B", personB.getName());
  assertEquals("C", personC.getName());

  }
}
スクリーンショット 2017-11-26 15.56.17 スクリーンショット 2017-11-26 15.56.24

ちゃんと命令網羅出来ていることがグリーンでわかる!黄色は分岐網羅が出来ていないところ ※C0ベースでグリーンは評価されているから処理bには入っていないけどグリーンになっているので注意、分岐のイエローを確認して分岐網羅を行ないにいく

最後にBを通してみる(C1:分岐網羅)

スクリーンショット 2017-11-26 15.57.08 スクリーンショット 2017-11-26 15.57.16

オールグリーンになりました!

最後に・・・・・

この手のツールは組合せの仕方によって気づいたら目的に沿った使用方法じゃなくなっていた・・・みたいな事があるので注意して使っていきたいと思います!