Robolectric+mockito
Robolectric+powermockitoでRobolectricを利用している際におけるstaticメソッドのモック化っていうのを書いたけど、単純にRobolectric+Mockitoを使ったインスタンスメソッドのモック化に関してはスルーしてたので色々検証
まぁ通常であればRobolectricでActivityをテストするのには
package shareroid.app;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SampleActivityTestCase {
@Test
public void 通常テスト() throws Exception {
SampleActivity activity = Robolectric.buildActivity(SampleActivity.class)
.create()
.start()
.get();
assertThat(activity.mTextView, notNullValue());
assertThat(activity.mTextView.getText().toString(), is("aG9nZQ"));
}
}
んな感じで実行するのだけど、Robolectric.buildActivityの返り値はorg.robolectric.util.ActivityController
まぁそこは良いとして、要点としては
- Robolectric.buildActivityを利用するのでMockitoを使ってどうやってモック化するのか
- onCreateが走る前にonCreateメソッドが利用する他のメソッドをモック化する場合はどうすれば良いのか
- 上記のようにRobolectricクラスを使ってonStartまでを処理させず自分でやりたい場合はどうすれば良いのか
っていう所。1に関してはMockito.spyを使えば良いはずなので、2と3に関して色々やってみる。本題はここから
その前に対象となるActivityは
package shareroid.app;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class SampleActivity extends Activity {
TextView mTextView;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
mTextView = new TextView(this);
mTextView.setText(buildText());
}
@Override
protected void onStart() {
super.onStart();
mTextView = new TextView(this);
mTextView.setText(buildText());
}
String buildText() {
return Sample.encodeB64("hoge");
}
}
onCreateをRobolectric側で作用させずにモック化してから実行する場合
※onStartは使わないのでコメントアウト
まぁただTextViewなインスタンス作って、それのテキストにbuildTextを使うだけ。でそのbuildTextメソッドをモック化して返す値を変えたりするだけ
package shareroid.app;
import org.junit.Test;
import org.junit.runner.RunWith;
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.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.util.ActivityController;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
@PrepareForTest(SampleActivity.class)
@PowerMockIgnore({ "android.*", "org.robolectric.*" })
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SampleActivityTestCase {
@Test
public void onCreateTest() throws Exception {
ActivityController<sampleActivity> controller = Robolectric
.buildActivity(SampleActivity.class);
// onCreateをあとで実行する場合には一度attachをしておかないとNPEが発生する
SampleActivity activity = controller.attach().get();
activity = spy(activity);
doAnswer(
new Answer<string>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
return "hoge";
}
}
).when(activity).buildText();
activity.onCreate(null);
assertThat(activity.mTextView, notNullValue());
assertThat(activity.mTextView.getText().toString(), is("hoge"));
verify(activity).buildText();
}
}
onStartをRobolectricで作用させずにモック化する場合
package shareroid.app;
import org.junit.Test;
import org.junit.runner.RunWith;
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.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.util.ActivityController;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
@PrepareForTest(SampleActivity.class)
@PowerMockIgnore({ "android.*", "org.robolectric.*" })
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SampleActivityTestCase {
@Test
public void onStartTest() throws Exception {
ActivityController<sampleActivity> controller = Robolectric
.buildActivity(SampleActivity.class);
SampleActivity activity = controller.create().get();
activity = spy(activity);
doAnswer(
new Answer<string>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
return null;
}
}
).when(activity).buildText();
activity.onStart();
assertThat(activity.mTextView, notNullValue());
assertThat(activity.mTextView.getText().toString(), isEmptyOrNullString());
verify(activity).buildText();
}
}
まぁこれに関しては特にツッコミどころ無い。ActivityController.startをしないだけで自分でonStartするだけだし。ActivityController.createはやってるけど、それもやらないのであればonCreateな時と同様に一度attachをしておく必要がある
無難なのはonCreateでモック化する必要とするようなケースの仕様にしない方がテストもすんなり出来るんじゃないかなっては思う所。onCreateでグダグダしてもねぇって所も