blog.petitviolet.net

AndroidでAssertJを使ってテストする

2015-08-10

QiitaJavaAndroid

最近知った AssertJ というテストライブラリを使ってみた AssertJ / Fluent assertions for java

Fluent assertions

とあるように流れるような assertion が出来ると聞いてちょっと気になった

AssertJ の導入

gradle の設定

AssertJ / Fluent assertions for java このページによると Android だと 1.x 系がいいらしい 3.x まで出ているので何となく残念な気持ちになる

build.gradle
dependencies {
    androidTestCompile 'org.assertj:assertj-core:1.+'
}

これで導入できる いくつかのファイルをandroid.packagingOptionsexlucde指定する必要があるかも知れない その場合は以下のようにすればまず問題ない

build.gradle
android {
    packagingOptions {
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
        exclude 'NOTICE.txt'
        exclude 'LICENSE.txt'
        exclude 'DEPENDENCIES'
        exclude 'NOTICE'
        exclude 'LICENSE'
        exclude 'asm-license.txt'
    }
}

テストの書き方

テストファイルでassertThatを import する

import static org.assertj.core.api.Assertions.assertThat;

これで例えばあるオブジェクトがnullかどうかをチェックするなら以下のように書ける

assertThat(hogeObject).isNotNull();

このようにassertThat(<テスト対象>).isXXX(<期待値>)のように流れるように書けるため、書きやすく読みやすいと感じる

また、正規表現で文字列をassertThatしたいとき、junit だとMatcherクラスを自分で実装したりする必要があった 例えばこんな感じ

RegexStringMatcher.java
class RegexStringMatcher extends BaseMatcher {
    private final Pattern mExpectedPattern;

    public RegexStringMatcher(String expected) {
        mExpectedPattern = Pattern.compile(expected);
    }

    public static RegexStringMatcher isMatch(String expected) {
        return new RegexStringMatcher(expected);
    }

    @Override
    public boolean matches(Object actual) {
        String actualStr = (String) actual;
        Matcher matcher = mExpectedPattern.matcher(actualStr);
        return matcher.find();
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("pattern: ").appendText(mExpectedPattern.pattern());
    }
}

このRegexMatcherを import してassertThat(hogeString, RegexStringMatcher.isMatch(pattern));のようにする必要があった これが AssertJ だとassertThat(hogeString).matches(pattern);のようにさくっと書けて最高

よく使うのはisEqualTo,isNotNull,isNull,isInstanceOf,hasSize,matchesといったところ。 いろいろあるのでAssertJ の公式を参照

View のテスト

導入

いつものごとく、Square 様が android 用にライブラリを提供している

build.gradle
dependencies {
	androidTestCompile 'com.squareup.assertj:assertj-android:1.0.1'
}

これで`‘org.assertj:assertj-core’も導入される

テストの書き方

普通のassertThatと大体同じに書ける AssertJ Android by Square, Inc.のページにあるようにisGoneisVisibleが簡単に書けることが特長であるといえる

findViewByIdなどして取得した View オブジェクトに対して例えばassertThat(<何かのView>).isVisible()assertThat(<何かのView>.getVisibility()).isEqualTo(View.VISIBLE)と同じ事が出来る

Espresso との比較

個人的には View のテストは AssertJ より Espresso の方が楽かなという印象 View のテストをそれぞれでやったテストファイル全体を載せるとこんな感じ

ExampleActivityTest.java
import android.app.Activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
import android.widget.Button;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static org.assertj.android.api.Assertions.assertThat;

@RunWith(AndroidJUnit4.class)
@MediumTest
public class ExampleActivityTest {
    Activity mActivity;
    @Rule
    public ActivityTestRule<StartActivity> activityRule = new ActivityTestRule<>(ExampleActivity.class);

    @Before
    public void setUp() throws Exception {
        mActivity = activityRule.getActivity();
        assertThat(mActivity).isNotNull();
    }

    @Test
    public void 画面が表示されているかどうか() {
        onView(isDisplayed());
    }

    @Test
    public void ボタンのテストwithEspresso() {
        onView(withId(R.id.awesome_button)).check(matches(isDisplayed()));
        onView(withId(R.id.awesome_button)).perform(click());
    }

    @Test
    public void ボタンのテストwithAssertJ() {
        final Button button = (Button) mActivity.findViewById(R.id.awesome_button);
        assertThat(button).isNotNull().isVisible();
        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                button.performClick();
            }
        });
    }
}

AssertJ を使うと一旦findViewByIdをしてインスタンスを取得して、それに対して assertion を行うことになる また、上に示したように例えばperfomeClickするためにはActivity#runOnUiThreadを介する必要が生じてしまう

それに対して Espresso であればonView(withId(<id>)).perform(click())だけで書けるため、コード量も減ることとなる

本当に簡単な null チェックや visiblity チェックだけのテストであれば AssertJ でいいと思うが、ちょっと細かいことをするならあまりおすすめ出来ない感じではある

所感

先日の Android オールスターズでの発表をきいて AssertJ の存在を知った RECRUIT TECHNOLOGIES Member’s blog Android オールスターズでテストの話をしました

JUnit よりも AssertJ の方が個人的には流れるように書けるため、書きやすく読みやすいように感じる

Java のテストには AssertJ がオススメ - Qiita こちらの記事で紹介されているようにカスタム Assertion の実装も簡単に出来て、個人差はあると思うが全体的に JUnit よりちょっと良い、という感覚

また、公式でも JUnit からのマイグレーションについて記述があり、比較的移行も簡単だと思う convert your JUnit assertions to AssertJ とりあえず Android Studio の置換窓に

  • before - assertThat\((.+), is\((.+)\)\);
  • after - assertThat\($1\).isEqualTo\($2\);

を入れればある程度置換出来た すでに書かれたテストを置換するほどではないが、新しくテストを書き始めるならこれからはこちらを使えばいいと思う。

from: https://qiita.com/petitviolet/items/c95255bf3e1ccd9766c3