CircleCIでjacocoのカバレッジレポートをとる #circleCi #jacoco #java

概要

SpringBoot + Spock + maven で開発をしているんだけれど、せっかくテストも書いているのでカバレッジレポートをCI時に取得するようにする。

pom.xmlにjacocoプラグインを追加

...
<build>
    ...
    <plugins>
        ...
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.7.9</version>
            <executions>
                <execution>
                    <id>prepare-agent</id>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
</build>
...
<reporting>
    <plugins>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <reportSets>
                <reportSet>
                    <reports>
                        <report>report</report>
                    </reports>
                </reportSet>
            </reportSets>
        </plugin>
    </plugins>
</reporting>
...
  • execution を設定しておけば、test時に自動で prepare-agent を実行してくれる
  • reporting は必要なのかどうか実はよく分かっていない。。
  • これで mvn clean test jacoco:report とかやると、target配下にレポートが出力されるようになる

circle.ymlの修正

test:
  override:
    - mvn integration-test
    - mvn jacoco:report
  post:
    - mkdir -p $CIRCLE_ARTIFACTS/site/jacoco
    - mv target/site/jacoco/ $CIRCLE_ARTIFACTS/site/jacoco
  • testブロックでカバレッジレポートを出力するようにする
  • testのpostブロックで $CIRCLE_ARTIFACTS のレポート成果物を配置する

そうすると、下記のような感じでレポートが参照できるようになる。

https://26-80196016-gh.circle-artifacts.com/0/tmp/circle-artifacts.RCejr3V/site/jacoco/jacoco/index.html

なお、test-postブロックはtestの成否に関わらず実行されてしまうので非効率であるのだが、とりあえずはこのままでよしとしている。

まとめ

実際に適用したプロジェクトは下記。
GitHub - su-kun1899/mysql-visualizer: Mysql Metadata Viewer

jacocoのレポート、UIはパット見時代を感じさせるのだけれど、見やすく分かりやすくて良い。

f:id:su-kun1899:20170417232708p:plain

f:id:su-kun1899:20170417232642p:plain

参考

flywayで差し込みバージョンを適用する #flyway

こんな感じの時、間に差し込みバージョンを作る。

+------------+-----------------+---------------------+---------+
| Version    | Description     | Installed on        | State   |
+------------+-----------------+---------------------+---------+
| 1.0        | init            | 2017-01-01 10:00:00 | Success |
| 2.0        | add2            | 2017-01-02 10:00:00 | Success |
| 3.0        | add3            | 2017-01-03 10:00:00 | Pending |
+------------+-----------------+---------------------+---------+

flyway:infoすると、ignoredとして認識される。

+------------+-----------------+---------------------+---------+
| Version    | Description     | Installed on        | State   |
+------------+-----------------+---------------------+---------+
| 1.0        | init            | 2017-01-01 10:00:00 | Success |
| 1.1        | patch1          | 2017-01-02 10:00:00 | Ignored |
| 2.0        | add2            | 2017-01-02 10:00:00 | Success |
| 3.0        | add3            | 2017-01-03 10:00:00 | Pending |
+------------+-----------------+---------------------+---------+

この状態でflyway:migrateすると、エラーになる。

下記はMavenプラグインで実行した場合。

[ERROR] Failed to execute goal org.flywaydb:flyway-maven-plugin:3.2.1:migrate (default-cli) on project shotaki: org.flywaydb.core.api.FlywayException: Validate failed. Detected resolved migration not applied to database: 1.1 -> [Help 1]

outOfOrderオプションをtrueにして実行すると、Ignoredも含めて実行してくれる。

Mavenプラグインコマンドラインで直接指定だと下記のような感じ。

mvn flyway:migrate -Dflyway.outOfOrder=true

この方法で実行すると、当該バージョンとそれ以降で適用済だったバージョンのStateがOutOrdrになる。

+------------+-----------------+---------------------+---------+
| Version    | Description     | Installed on        | State   |
+------------+-----------------+---------------------+---------+
| 1.0        | init            | 2017-01-01 10:00:00 | Success |
| 1.1        | patch1          | 2017-01-02 10:00:00 | OutOrdr |
| 2.0        | add2            | 2017-01-02 10:00:00 | OutOrdr |
| 3.0        | add3            | 2017-01-03 10:00:00 | Success |
+------------+-----------------+---------------------+---------+

参考

FlywayでDBスキーマのマイグレーションをしてみたsiguniang.wordpress.com

IntelliJ IDEAでフォーマットを一部無効にする #IntelliJIdea

コードのフォーマットは基本IDEAにおまかせなのですが、どうしても一部フォーマットをカスタマイズしたいことはあると思います。

JavaのStreamでごりごりやる時とか、Spockのwhereブロックとか。

んで、せっかくキレイにしたのにうっかりファイルにフォーマットかけてしまってガタガタに。。みたいなこともIDEAならなんとかしてくれます。

formatter markersを有効にする

Code Styleから「Enable formatter markers in comments」を有効にします。

f:id:su-kun1899:20170402130339p:plain

こうすると @formatter:off を書いたコメントの配下にあるコードはフォーマットされません。

...フォーマットされる...
// @formatter:off
...フォーマットされない...
// @formatter:on
...フォーマットされる...

参考

stackoverflow.com

Eachの中でassertするときは明記する必要がある #spock

Spockでは通常、thenブロックの中はbool値を返すようにしておけば勝手にアサーションしてくれる。

then:
actual == expect

ただし、イテレーションの中はアサーションしてくれない

then:
actualArray.each {
  it.getName == expectName // falseでもテストNGにならない
}

明示的にassertを記載すればちゃんとアサーションされる。

then:
actualArray.each {
  assert it.getName == expectName
}

Mavenで特定のクラスのテストだけ実行する #maven #java

特定のテストだけ

mvn test -Dtest=red.sukun1899.SampleSpec

クラス名が一意になるなら、パッケージは省略できる

mvn test -Dtest=SampleSpec

複数の場合はカンマでつなぐ

mvn test -Dtest=HogeSpec,FugaSpec

特定のメソッドだけ実行する場合シャープでつなぐ

mvn test -Dtest=SampleSpec#test1

複数メソッドの場合カンマでつなぐ

mvn test -Dtest=SampleSpec#test1,SampleSpec#test2

メソッド名に空白が含まれる場合、ダブルクォートで括る

mvn test -Dtest="SampleSpec#test するよ, SampleSpec#test しちゃうよ"

正規表現っぽいのも使える

mvn test -Dtest="SampleSpec#test*"

TDDっぽくモブプログラミングしてみた #MobProgramming

最近はまっていることがあるんですけど。。と同僚に言ったら「モブプログラミングですか?」と聞かれる程度にはハシャいでおります。

先日はTDDな感じでやってみました。

(結果的にそうなったけれど、そうしようとして始めた感じではなかったかも)

ちょっと複雑な処理があって、パターンから考えたほうがいいかもという話になり、ホワイトボードを使って思考を整理して、テストケースにまで落とし込む。

そしたらまずテストコードを書いてこけるところまで確認してから、あーでもないこうでもないと進めました。

試行錯誤が必要なときに、最初からテストがあると安心感が違います。

ゴールが決まっていることで、出口のない迷路に迷い込んでしまうこともない。

これはTDDのいいところですね。

結構複雑なタスクだったので、本格的なリファクタリングまでは取組めなかったものの、数時間でテストがグリーンになるところまで進めたのは正直驚きがありました。

複雑さに立ち向かうとき、モブプログラミングとTDDの掛け合わせは効果が高そうなのでおすすめです。

モブプログラミング楽しいですよ、モブプロ。

みんなで思考整理をするの図

f:id:su-kun1899:20170402125921p:plain

データ抽出のテストにはモブプログラミングを使うといいかも #MobProgramming

モブプログラミングとは?

この記事では複数人で行われるプログラミングの意です。

ペアプロの延長線上だと考えて下さい。

正確な定義とは異なるかもしれません。

参考までに以前書いた記事です。

su-kun1899.hatenablog.com

データ抽出のテストって?

何らかのデータストアに対して、クエリを発行し、結果を取得するようなUnitTestを想定しています。

MySQLようなRDBMSに、Select文を発行して結果を確認するようなイメージです。

データ抽出のテストの何が問題なの?

データ抽出のテストは実装後のレビューコストが高いです。

データ抽出する処理の実装時に考慮すべきことに例えば下記があると思います。

  • データストアの設計
    • RDBMSでいうER。オブジェクト間のリレーション。仕様的なものも含む
  • データ
    • どんなテーブルに、どんなデータが入っているか
  • クエリの構造
    • JOIN句の類。内部結合か外部結合か。UNIONやソート順などなど
  • パラメータ
    • どんな条件で検索するか。SelectでいうWhere句
  • etc

多くの場合、実装者はこれらを並行で考慮しながらテストを書く必要があります。

そして、主にデータパターンとパラメータの組み合わせでテストの意図が込められていきます。

意図の読み解きが難しい

データパターンやパラメータの場合、コードから意図を読み解くことは一般的に難しいと思います。

レビュワーも当然前述の項目を並行で考えなければならないからです。

ケースは思い浮かんでも、どこで実施しているかを読み解くのは困難です。

DbSetupのようにテストデータの可視性を高めるライブラリもありますが、限界はあります。

そのため、後からコードレビューを行う際に、どうしてもコストが高くなってしまうのです。

なぜモブプログラミングがよいの?

モブプログラミングではリアルタイムでレビューを行います。

そのため、ドライバーが意図を説明しながら、ナビゲーターが意図を理解しながら同時進行で進めることができます。

なので、実装とレビューでフェーズを分ける場合に比べて効率が格段に上がると思います。

僕らはさらに、着手前にチームメンバーで想定されるテストケースの洗い出しをしました。

作業を進めていく中で、実はいらなかったケースなどが出てきたりして、とても効果的だったと思います。

まとめ

性質上、コードから意図の読み取りが難しくなるような場合は、モブプログラミングをおすすめします。

モブプログラミング楽しいですよ、モブプロ。

みんなでテストケースを洗い出すの図

f:id:su-kun1899:20170330005933p:plain