petitviolet blog

    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