Javaで期間の日数を計算する #java

概要

2016年1月7日から2017年4月27日みたいな期間の日数を数える方法。

Period…惜しい

日付の期間を扱うには java.time.Period クラスがあるが、悲しいことに日数をカウントするAPIがない。

getDays() は日付だけで計算をするため、月をまたいだ日数のカウントにはならない。

Period period = Period.between(LocalDate.of(2017, 1, 1), LocalDate.of(2017, 2, 1));
int count = period.getDays(); 
System.out.println(count); // 31ではなく、0が返ってくる

ちなみに月は getMonths() とは別に期間の合計月数を取得できる toTotalMonths() がある。

なぜ toTotalDays() はないのだ。。

Durationならいける

時間ベースの期間を表す java.time.Duration クラスであれば、日数をカウントできる。

Duration duration = Duration.between(LocalDate.of(2017, 1, 1).atTime(0, 0, 0), LocalDate.of(2017, 2, 1).atTime(0, 0, 0));
long count = duration.toDays();
System.out.println(count); // 31!

※LocalDateTimeで最初からインスタンスを作っても良いが、日付の意図を示すためにあえて atTime() を使用

ChronoUnitでもOK

期間を表現する必要がなくて、本当に日数をカウントするだけなら java.time.temporal.ChronoUnit でよさそう。

long count = ChronoUnit.DAYS.between(LocalDate.of(2017, 1, 1), LocalDate.of(2017, 2, 1));
System.out.println(count); // 31!

参考

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