概要
このエントリでは、ソースコードを生成してくれるLombokを使っている前提で、カバレッジ計測ツールのJacocoでのカバレッジを上げていく方法について、サンプルを用意して紹介します。
このエントリの目的は、Lombokを使ってなるべくコードを書かないで、かつJacocoでのカバレッジを上げていくときの、とっかかりの参考となることを目的としています。それぞれのツールの全ての機能を解説するものではありません。
結論
結論から言うと、Jacoco0.8.0(2018/1/2リリース)以上を使って、lombok側で「lombok.addLombokGeneratedAnnotation = true」を指定することで、自動生成されたコードをカバレッジ検査対象から除外しましょう、というのが大きなポイントです。
参考エントリ
Qiita内では、Lombok, Jacocoについて、
たくさん(2018/5/26時点)ありますが、下記が参考になります。
初期サンプル
サンプルとして、Spring Bootで、REST Controller越しに簡単なエンティティを格納したり表示したりするものを作りました。
こんな感じの動作をするものです。
(登録)
$ curl -H 'Content-Type:application/json' -d '{"intField":123}' http://localhost:8080
(参照)
$ curl http://localhost:8080/
[{"id":"2815cfc6-f908-4c7d-a20b-b2c52953555f","intField":123,"longField":0,"stringField":null,"charField"
(1件詳細)
$ curl http://localhost:8080/2815cfc6-f908-4c7d-a20b-b2c52953555f
{"id":"2815cfc6-f908-4c7d-a20b-b2c52953555f","intField":123,"longField":0,"stringField":null,"charField":"\u0000","byteField":0}hirokis-M
初期状態のリポジトリに格納しています。
この版では、lombokのお便利機能を使わず実装しています。
$ ./gradlew test
$ ./gradlew jacocoTestReport
lombok適用
@ Data
適用前のSampleEntity
public class SampleEntity {
private SampleId id;
private int intField;
private long longField;
private String stringField;
private char charField;
private byte byteField;
/**
* @return the id
*/
public SampleId getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(SampleId id) {
this.id = id;
}
/**
* @return the intField
*/
public int getIntField() {
return intField;
}
/**
* @param intField the intField to set
*/
public void setIntField(int intField) {
this.intField = intField;
}
/**
* @return the longField
*/
public long getLongField() {
return longField;
}
/**
* @param longField the longField to set
*/
public void setLongField(long longField) {
this.longField = longField;
}
/**
* @return the stringField
*/
public String getStringField() {
return stringField;
}
/**
* @param stringField the stringField to set
*/
public void setStringField(String stringField) {
this.stringField = stringField;
}
/**
* @return the charField
*/
public char getCharField() {
return charField;
}
/**
* @param charField the charField to set
*/
public void setCharField(char charField) {
this.charField = charField;
}
/**
* @return the byteField
*/
public byte getByteField() {
return byteField;
}
/**
* @param byteField the byteField to set
*/
public void setByteField(byte byteField) {
this.byteField = byteField;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + byteField;
result = prime * result + charField;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + intField;
result = prime * result + (int) (longField ^ (longField >>> 32));
result = prime * result
+ ((stringField == null) ? 0 : stringField.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SampleEntity other = (SampleEntity) obj;
if (byteField != other.byteField)
return false;
if (charField != other.charField)
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (intField != other.intField)
return false;
if (longField != other.longField)
return false;
if (stringField == null) {
if (other.stringField != null)
return false;
} else if (!stringField.equals(other.stringField))
return false;
return true;
}
/**
* @param id
* @param intField
* @param longField
* @param stringField
* @param charField
* @param byteField
*/
public SampleEntity(SampleId id, int intField, long longField,
String stringField, char charField, byte byteField) {
super();
this.id = id;
this.intField = intField;
this.longField = longField;
this.stringField = stringField;
this.charField = charField;
this.byteField = byteField;
}
public SampleEntity() {
super();
};
}
こうなります。
lombokプラグインが適用されたIDE(このエントリではEclipse)で確認すると、setter, getter, toString, hashCodeが生成されていることが確認できます。

@ RequiredArgsConstructor
Springの@Autowiredでフィールドをインジェクションしていると、テストなどので直接差し替えが面倒になったり、使っているものが多くなってきたときに場所をとったりということがあるので、コンストラクタでインジェクションする形に変えてみます。
適用前のSampleApi.javaが
@RestController
@RequestMapping("/")
public class SampleApi {
@Autowired
private SampleFacade sampleFacade;
こうなります。
@RestController
@RequestMapping("/")
@RequiredArgsConstructor
public class SampleApi {
private final SampleFacade sampleFacade;
ここまでで、jacocoReportはこんな感じになります。

ここで、domainパッケージが結構な割合になっているわけですが、中をみれば当然ながらlombokで自動生成されたコードなわけです。lombokの動作を正とする時、生成されたものをテストすることにはあまり意味はありません。

このような理由で、Lombokで生成されたコードをテストから除外する指定をしてみます。
プロジェクトのディレクトリに下記のファイルを置き、もう一度テストを実施してみます。
lombok.addLombokGeneratedAnnotation = true
$ ./gradlew clean
$ ./gradlew test
$ ./gradlew jacocoTestReport
で、カバレッジがこのように変わります。テストコードをまだ書いていないので赤い状況は変わりまえんが、domainのところが大きく減り、全体のコードのInstructionが、731から170に大きく減っています。

lombok.configに指定可能な値の説明は、lombokの公式サイトに説明があります。
ここまでのものをGitHubにv0.2として置きました。
テストを追加
上記のものに、単体テストで、とりあえず1パスは通るようにしてカバレッジを100%にした状態が、GitHub上のサンプルv0.3になります。

(参考)これで、除外設定をもとにもどす(指定しない)と、このような状況になります。

まとめ
このエントリでは、lombokで自動生成されたコードをカバレッジの計測から除外する方法を確認し、実際に動作するところまでをサンプルコードを通して示しました。
lombokの機能は本エントリのサンプルで使った以外にも本家のサイトでいろいろ紹介されているので、別の機会に利用例を紹介したいと思います。