かまたま日記3

プログラミングメイン、たまに日常

GradleプラグインをGradle community portalにアップロードした

今まで、自作の2つのGradleプラグインGitHub上のオレオレMavenリポジトリから落とすようにしていたんですが、 buildscript でそのリポジトリを指定しないと行けなかったり、記述がちょっとだけ面倒だったので、Gradle community portalにアップしてPlugins DSLで書けるようにしました。

アップロードの方法は簡単で、基本的にこちらの手順に従うだけです

plugins.gradle.org

  • ポータルのアカウントを作る
  • APIキーを作成
  • Gradle plugin publishing pluginを使って諸々の設定をbuild.gradleに書く (ref)
  • publishPlugin タスクを実行

初回のみapprovalが必要でちょっと時間がかかりますが、自分の場合は半日くらいで承認されました。

JUnit 5 入門

そろそろ使ってみるかということで入門してみました。

JUnit Jupiter

こちらにも書かれてますが、JUnit 5は複数のサブプロジェクトからなり、JUnit 5でテストを書いたり拡張機能を書くためのクラスはJUnit Jupiterというプロジェクトにあります。なので、テストを書く場合は org.junit.jupiter 配下の各種ライブラリーをインポートして使うことになります。

Gradle から使う

最低限以下の記述が必要になります。 (Gradle 4.6以上が必要です)

dependencies {
    testCompile('org.junit.jupiter:junit-jupiter-api:5.3.2')
    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
}

test {
    useJUnitPlatform()
}
  • junit-jupiter-apiJUnit Jupiter のテストを書くのに必要なクラス、アノテーション群があります。 testCompile で指定します。
  • junit-jupiter-engineJUnit Jupiter のテストを実行するための TestEngine 実装です。testRuntime で指定します。
  • testタスク内のコンフィグレーションで useJUnitPlatform を指定することで、JUnit 5のプラットフォームを使うように宣言します。

テストを書く、実行する

基本的な書き方はJUnit 4までと同じで、テストしたいメソッドに @Testアノテーションを追加します。アサーションorg.junit.jupiter.api.AssertionsクラスにJUnit 3まででお馴染みの assertEquals などの基本的なアサーションメソッドがあるので、それを使います。JUnit 4時代の assertThat や、AssertJなどの別のアサーションライブラリを使いたい場合は、別途HamcrestやAssertJをインストールして使うことができます。

package com.example.project;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class SampleTests {
    @Test
    void onePlusOneEqualsTwo() {
        assertEquals(2, 1 + 1);
    }
}

実行はGradleのテストタスクで実行します。

$ ./gradlew test

> Task :test

com.example.project.SampleTests > onePlusOneEqualsTwo() PASSED

BUILD SUCCESSFUL in 1s
3 actionable tasks: 2 executed, 1 up-to-date

ちょっと高度な使い方集

BeforeとかAfterとか

@Before @After@BeforeEach @AfterEach に、 @BeforeClass @AfterClass@BeforeAll, @AfterAll に置き換えられました。

package com.example.project;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class DBTests {
    @BeforeAll
    static void initializeDB() {
        System.out.println("Initializing Database...");
    }

    @AfterAll
    static void deleteDB() {
        System.out.println("Deleting Database...");
    }

    @BeforeEach
    void insertData() {
        System.out.println("Inserting test data...");
    }

    @AfterEach
    void clearData() {
        System.out.println("Clearing test data...");
    }

    @Test
    void testWithDB() {
        System.out.println("Testing...");
    }
}

Parameterized Test

junit-jupiter-params のライブラリをインストールした上で @ParameterizedTest をテストメソッドに追加します。パラメタのソースは、簡易的には @CsvSourceCSV文字列で指定できます。もうちょっと高度にやりたい場合は @ArgumentsSource アノテーションを使うことで独自の引数のProviderを指定することができます。

dependencies {
    ....
    testCompile('org.junit.jupiter:junit-jupiter-params:5.3.2')
    ....
}
package com.example.project;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.junit.jupiter.api.Assertions.assertEquals;

class ParameterizedTests {
    @ParameterizedTest(name = "{0} + {1} = {2}")
    @CsvSource({
            "0,    1,   1",
            "1,    2,   3",
            "49,  51, 100",
            "1,  100, 101"
    })
    void testsForPlus(int first, int second, int expected) {
        assertEquals(expected, first + second, first + " + " + second + " should equal " + expected);
    }
}

JUnit 4のテストを実行する

junit-vintage-enginetestRuntime でインストールします。これはJUnit 4以前のテストを実行するためのTestEngineの実装です。

dependencies {
    ...
    testCompile "junit:junit:4.12"
    testRuntime "org.junit.vintage:junit-vintage-engine:5.3.2"
    ...
}
package com.example.project;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;

public class JUnit4Tests {
    @Test
    public void test() {
        assertThat(1 + 2, is(3));
    }
}

拡張機能

JUnit 4の @Rule のような拡張機能は任意の Extension classを実装することで実現できます。 詳しくは この辺 参照。

package com.example.project;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.support.AnnotationSupport;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Optional;

class ExtensionTests {
    @Test
    @MyExtension("FOO")
    void extensionTest() {
        System.out.println("Running a test...");
    }

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @ExtendWith(MyExtensionImpl.class)
    private @interface MyExtension {
        String value();
    }

    private static class MyExtensionImpl implements BeforeEachCallback {
        @Override
        public void beforeEach(ExtensionContext context) {
            final Optional<MyExtension> annotation = AnnotationSupport.findAnnotation(context.getTestMethod(), MyExtension.class);
            System.out.println(String.format("Running my extension with %s...", annotation.get().value()));
        }
    }
}

参考

embulk-filter-hash v0.5.0 リリース

イシューで希望をくれたHMACのハッシュ化に対応しました。以下のような感じで algorithm にHMACのアルゴリズムを指定した上で secret_key秘密鍵を入れると使えます

filters:
  - type: hash
    columns:
    - { name: username }
    - { name: phone_number, algorithm: HmacSHA256, secret_key: passw0rd }

JavaでRubyのeach_sliceがしたい

each_sliceというのは配列を指定した要素数の配列に分ける処理です。リストの中身をn件ごとに処理するときに便利です。 Javaには同様の処理が(たぶん)標準APIには無いので、こんな感じで行けそうです。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Test
{
    public static void main(String[] args)
    {
        // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
        System.out.println(slice(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 3));
    }

    private static <T> List<List<T>> slice(List<T> list, int n)
    {
        final int resultSize = (int) Math.ceil((double) list.size() / n);
        return IntStream.range(0, resultSize)
                .mapToObj(i -> list.subList(n * i, Math.min(list.size(), n * (i + 1))))
                .collect(Collectors.toList());
    }
}

参考: Is there a way to do the Ruby each_slice in Java 8? - Stack Overflow

アメリカに行ってきた

先週1週間会社の出張でアメリカ(マウンテンビュー)に行ってきました。海外は前職でのフィリピン出張以来2年ぶりです。

飛行機

行きは羽田からサンフランシスコ国際空港(SFO)の直行便で9時間ほど、帰りは後述の理由からSFOから香港国際空港(HKG)経由の成田への便でした、香港での待ち時間含めて22時間くらい。中国は初上陸でしたが、HKGでWi-Fi使う分には普通にTwitterできました。

気候

滞在した一週間は雨も降らず、サンフランシスコもマウンテンビューも最低10℃最高25℃くらいで朝は肌寒く日中は過ごしやすい、過ごしやすい気候でした。"ベイエリアは湿度が低いので日本ほど暑く感じない" というのをよく聞いていましたが、実感しました。

食事

基本的にアメリカ基準のサイズですべてが大きかったです。会社のランチはEAT Clubというのを使っているのですが、サラダ+主菜で注文したらサラダが直径25cmくらいのボウルに並々入っていて単体でお腹いっぱいになる量でした。結局食べきれず、持って帰って夜食べていました...w

あと、マウンテンビュー最後の夜にSushitomiという店で寿司を食べましたが、日本のクオリティで普通に美味しかったです。近くのshalalaというラーメン屋も美味しいらしいので、今度MVに行った際は寄ってみたいです。

仕事

1週間と短い間の滞在で時差ボケもありあまり効率的に作業ができたとは言いがたかったですが、直属の上司やプロダクトマネージャー、QAチームなどと初めて直接顔を合わせてコミュニケーションできたので、今後の仕事のやり易さに繋がればなあと思います。

歯の詰め物が取れた

3日目に歯の詰め物が取れてしまいました。治療したかったのですが、HRの方とも相談して日本に帰国後治療することに。IT健保の場合、海外での医療費は後で申請すれば返金してもらえるようなのですが*1、日本の治療費基準で返金されるようなので、日本の3~10倍ほどすると言うUSでの歯科治療費を考えると、申請する手間と割にに合わないと判断しました。

Uber, Airbnb

今回の出張で初めてUberAirbnbを利用したのですが、なるほどこれは便利だなあという感じでした。特にUberは精算の手間も無くなるし日本でもUberもしくは同様なサービスが流行ってほしいなと思いました。

コンピュータ歴史博物館

マウンテンビューを経つ直前に行ってきました。そろばんから現代のコンピュータまでの計算機の歴史が一同に介しており、とても見ごたえがありました。時間がなく駆け足で見ることになってしまったので、また行ってじっくり見たいです。

余談

出張後に夏休みを取って、家族とアメリカ観光をする予定だったのですが、直前になり子供が熱を出して渡航できなくなってしまうと言うハプニングが。結局諸々の予定をキャンセルし、帰りの航空券も取り直し日本に帰ることになりました。ホテル、ツアーなど直前キャンセルが利かないものも多く、10%ほどしかお金は戻ってきませんでした(泣)

tfenvのオーナーを移管した

色々issueとかPRとかもらっていたのですが、転職も決まってTerraformユーザーではなくなってしまったこともあり、私自身がメンテナンスするモチベーションが上がらなくなっていたため、別の方にオーナーになってもらうことにしました。

2ヶ月ほどの募集期間と話し合いを経て、前からコミットしてくれていたZordrakさんに移管することになりました。

300以上のスターをもらって世界各地の人からissueやPRをもらうようなOSSを作るというのは光栄で、いい経験になりましたが、OSSを個人でメンテし続けるということの難しさも実感しました。唯一残念なのは、移管することで自分のGitHubプロフィールがスター数的に寂しくなってしまったことです(笑)

オーナーではなくなりましたが、今後もtfenvをご贔屓にしていただけると幸いです。