Robolectric+powermockito

2013-10-11T00:00:00+00:00 Android Java robolectric

参考: http://addie9000.blogspot.jp/2012/12/robolectricpowermockandroidunit-test.html

Robolectricを使ってJUnit4ベースでテストしたいけど、PowerMockitoを使ってstaticメソッドをテストしたいって思ってもRobolectric自体が@RunWith(RobolectricTestRunner.class)を使うので、PowerMockitoのPowerMockRunner.class指定出来ないよねっていう事で上記の参考にRobolectricでのPowerMockitoの動かし方参考にしつつやってみた。ただ気になる事の検証的な目的かなと

※但し、上記の参考のRobolectricバージョン(1.1)とこの記事のバージョン(2.2)は違う

必要なライブラリ

上記の参考に書いてる通りにライブラリを取ってくる。でまぁ

hamcrestは使わない人は要らないけど。で良くEclipseであるあるなケースとしてライブラリの照合順序によっては色々実行でずっこけたりする。参考でもandroid.jarを最後に読むこまないとダメとかっていうのがあるらしいけど、こっちでやったのだとそういう問題は出なかった。せいぜいJUnitに入ってるhamcrestが認識してhamcrest-allなやつが認識してくれないから照合順序を上げるとかそういうのは必要だった

んまぁあとは普通にテスト書けば良いんじゃねって事で適当に

アクティビティ及びテストを書く

package shareroid.app;

import android.util.Base64;

public class Sample {
    public static String encodeB64(String str) {
        return Base64.encodeToString(
            str.getBytes(),
            Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING
        );
    }
}

的なstaticメソッドを持つクラスがあって

package shareroid.app;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class SampleActivity extends Activity {

    TextView textView;

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        buildText();
    }

    void buildText() {
        if (textView == null) {
            textView = new TextView(this);
        }

        textView.setText(Sample.encodeB64("hoge"));
    }
}

的な感じで使われる(※AndroidManifest.xmlに定義しておく必要性は無い)。でこの場合にSample.encodeB64メソッドが返す値をPowerMockitoを使ってモックを差し込み返す値を変えたりした場合どうなるかとか

package shareroid.app;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;

import android.util.Base64;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import static org.powermock.api.mockito.PowerMockito.*;

@RunWith(RobolectricTestRunner.class)
@PrepareForTest(Sample.class)
@PowerMockIgnore({"org.robolectric.*", "android.*"})
public class SampleTestCase {

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    @Test
    public void test1() {
        // Sample.encodeB64をモックで何を指定されてもnullを返すようにする
        mockStatic(Sample.class);
        when(
            Sample.encodeB64(Mockito.anyString())
        ).thenAnswer(
            new Answer<string>() {
                @Override
                public String answer(InvocationOnMock invocation) throws Throwable {
                    Object[] args = invocation.getArguments();
                    assertThat(args.length, is(1));
                    assertThat((String)args[0], is("hoge"));
                    assertThat(
                        (String)invocation.callRealMethod(),
                        is(
                            Base64.encodeToString(
                                "hoge".getBytes(),
                                Base64.NO_WRAP | Base64.NO_PADDING
                            )
                        )
                    );

                    return null;
                }
            }
        );

        SampleActivity activity = Robolectric.buildActivity(SampleActivity.class)
            // invoke Activity.performCreate
            .create()
            .get();

        // 上記モックでnullが返されるので実質setText(null)が実行される
        assertThat(activity.textView.getText().toString(), isEmptyOrNullString());

        verifyStatic();
    }
}

まぁあんま意味のないケースなのであれですけど、これからも色々Robolectric使いつつのテストで分かった事とかレポートする予定なので(ry

ちなみに個人的にはPowerMockitoじゃなくてjmockitが使えればなぁって思う所 (まだ成功してないけど、もしかしたらバージョン関係な問題だったのかもっていう疑惑があるので)

Robolectric+mockito rubygemsサーバーを作る