Robolectric3.4.2+Powermock1.7.3でのテスト (2017版)
今まで何度か検証してみましたが、情報が2013年度と古く今で実際使えるのが微妙だったので2017年度版としてチャラい調査をしてみました
※コードのベースはRobolectric+powermockのを利用しています。あくまで検証用なので
Sample.java
Powermockでmockする為にわざとstaticメソッドななんかをでっち上げる
package kinjouj.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
);
}
}
上記に書いた以前の検証コードのまま
MainActivity.java
上記のSampleを使って画面に出すだけなActivity UIを作る
package kinjouj.app;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity 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"));
}
}
これも前回のまま。でSample.encodeB64メソッドをmockStaticしてごにょごにょした結果のtextViewからのテキストを検証するのをRobolectricを使って行う
root_project/build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
多分、Android Studioを使ってプロジェクトを生成したままのコード
root_project/app/build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "app.kinjouj.myapplication"
minSdkVersion 26
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.4.2"
testCompile 'org.powermock:powermock-core:1.7.3'
testCompile 'org.powermock:powermock-api-mockito2:1.7.3'
testCompile 'org.powermock:powermock-module-junit4:1.7.3'
testCompile 'org.powermock:powermock-module-junit4-rule:1.7.3'
testCompile 'org.powermock:powermock-classloading-base:1.7.3'
testCompile 'org.powermock:powermock-classloading-xstream:1.7.3'
}
※注意 powermock-api-mockitoではなくpowermock-api-mockito2です
これでテストできる環境が整ったんでテスト書く
SampleTestCase.java
package kinjouj.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.*",
"javax.xml.parsers.*", // これ入れないといけない模様。一個下も
"com.sun.org.apache.xerces.internal.jaxp.*"
})
public class SampleTestCase {
@Rule
public PowerMockRule rule = new PowerMockRule();
@Test
public void test1() {
mockStatic(Sample.class);
when(Sample.encodeB64(Mockito.anyString()))
.thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
// 実メソッドをコールしてみてテストしてみる
assertThat(
(String)invocation.callRealMethod(),
is(
Base64.encodeToString(
"hoge".getBytes(),
Base64.NO_WRAP | Base64.NO_PADDING
)
)
);
// コールされると返されるのは"ABC"
return "ABC";
}
});
MainActivity activity = Robolectric.buildActivity(MainActivity.class)
.create()
.get();
assertThat(activity.textView.getText().toString(), is("hoge"));
verifyStatic();
}
}
まぁテストを実行してレポートを見ると
まぁMainActivity#buildTextで指定してる「Sample.encodeB64("hoge")」としているのにもかかわらずmockしたメソッドで"ABC"しか返さないようになってるので
まぁめんどくさいのでアプリを作って実際にケースとして紹介するというところまでの気力は無かったので(ry
以上。とりあえずは現時点でのRobolectric3.4.2+powermock1.7.3を使ってテストするケースは終わり
追記: @RunWith(PowerMockRunner.class)を維持したままRobolectricを使う場合
PowerMockRunnerDelegateを使えばできるっぽい。てか公式に書いてた
package kinjouj.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.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
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(PowerMockRunner.class)
@PowerMockRunnerDelegate(RobolectricTestRunner.class)
@PrepareForTest(Sample.class)
@PowerMockIgnore({
"org.robolectric.*",
"android.*",
"javax.xml.parsers.*",
"com.sun.org.apache.xerces.internal.jaxp.*"
})
public class SampleTestCase {
@Rule
public PowerMockRule rule = new PowerMockRule();
// 以下省略