Mockitoはfinalとstatic methodをmocking、spyingをサポートしていません。 しかし、DexmakerのMockitoライブラリを利用すれば、Androidでfinal、static methodをmocking、spyingすることができます。
アンドロイドプロジェクトでDexmaker mockitoを設定し、final、static methodをmockingする例を紹介します。
Mockitoを利用して、テストコードを作成する基本的な方法は、Android Mockitoにテストコードを作成するを参照してください。
プロジェクトの設定
DexmakerはP OS以上のSDK(API 28)のみを使用することができます。 P以前のバージョンでは使用できません。
プロジェクトを作成すると、Appの build.gradle
に次のようにJava 8
をサポートするようにします。
android {
...
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
...
}
そして build.gradle
の依存性には、次のようにライブラリを追加します。
dexmakerは、内部的に org.mockito:mockito-core
を参照するため、他のライブラリは、別途追加しなくてもされます。
dependencies {
...
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline-extended:2.25.1'
}
Mocking final method
final methodをmockingする例です。 既存のMockitoでnon-finalメソッドをmockingする方法と同じです。
class FinalTrojan {
final String finalOpen() { return "horse"; }
}
@Test
public void testStubbingFinalMethod() {
FinalTrojan mockF = mock(FinalTrojan.class);
when(mockF.finalOpen()).thenReturn("soldiers");
assertEquals("soldiers", mockF.finalOpen());
}
Mocking static method
staic methodをmockingする例です。
private static class SuperClass {
final String returnA() {
return "superA";
}
static String returnB() {
return "superB";
}
static String returnC() {
return "superC";
}
}
@Test
public void mockingExample() {
assertEquals("superB", SuperClass.returnB()); // 1
MockitoSession session = mockitoSession()
.mockStatic(SuperClass.class).startMocking(); // 2
try{
assertNull(SuperClass.returnB()); // 3
when(SubClass.returnB()).thenReturn("fakeB"); // 4
assertEquals("fakeB", SuperClass.returnB()); // 5
} finally {
session.finishMocking(); // 6
}
}
- mockingない場合は、実際のクラスで実装された値が返されます。
- MockitoSessionオブジェクトにクラスをmockingます。
- mockingが開始されると、メソッドはnullを返します。
- when、thenReturnを利用して、mockingを実装します。
- メソッドを呼び出したとき、設定した値が返されることを確認します。
- mockingが終わったら
finishMocking()
を呼び出して終了します。
mockingは
mockStatic(SuperClass.class)
のように、spyingはspyStatic(SuperClass.class)
のように使用します。
Mocking Subclass
Subclassをmockingする例です。
private static class SuperClass {
final String returnA() {
return "superA";
}
static String returnB() {
return "superB";
}
static String returnC() {
return "superC";
}
}
private static final class SubClass extends SuperClass {
static String recorded = null;
static String returnC() {
return "subC";
}
static final String record(String toRecord) {
recorded = toRecord;
return "record";
}
}
@Test
public void mockOverriddenStaticMethod() throws Exception {
MockitoSession session = mockitoSession().mockStatic(SubClass.class).startMocking();
try {
// By default all static methods of the mocked class should return the default answers
assertNull(SubClass.returnB());
assertNull(SubClass.returnC());
// Super class is not mocked
assertEquals("superB", SuperClass.returnB());
assertEquals("superC", SuperClass.returnC());
when(SubClass.returnB()).thenReturn("fakeB");
when(SubClass.returnC()).thenReturn("fakeC");
// Make sure behavior is changed
assertEquals("fakeB", SubClass.returnB());
assertEquals("fakeC", SubClass.returnC());
// Super class should not be affected
assertEquals("superB", SuperClass.returnB());
assertEquals("superC", SuperClass.returnC());
} finally {
session.finishMocking();
}
// Mocking should be stopped
assertEquals("superB", SubClass.returnB());
assertEquals("subC", SubClass.returnC());
}
人の子がいるstatic method
引数があるstatic methodをmockingする例です。
static class S {
static String staticEcho(String in) {
return in;
}
}
@Test
public void testStubbingStaticMethod() {
MockitoSession session = mockitoSession().mockStatic(S.class).startMocking();
try {
when(S.staticEcho("Marco")).thenReturn("Polo");
assertEquals("Polo", S.staticEcho("Marco"));
} finally {
session.finishMocking();
}
// Once the session is finished, all stubbings are reset
assertEquals("Marco", S.staticEcho("Marco"));
}
Verify static method
static methodをverifyする方法です。
ここmockingではなく、spyingをしました。 mockingはクラス内に実装されたメソッドをstubになりますが、spyingは実装されたコードをそのまま使用するという違いがあります。
verifyは、指定したメソッドが呼び出されたか、何度呼び出されていることを確認する方法です。 行為(Behavior)の観点からテストを実行することができます。
verify()
は予想される回数と実際に呼び出される回数が異なる場合、テストに失敗として処理します。
以下は、 UtilClass
クラスをspyingし、staticメソッドが何度呼び出されるverifyするコードです。
static class UtilClass {
public static String staticMethod(String str) {
return str;
}
public static void staticVoidMethod(String str) {
staticMethod(str);
}
}
@Test
public void testVerifyStaticMethod() {
MockitoSession session =
mockitoSession().spyStatic(UtilClass.class).startMocking();
try {
UtilClass.staticVoidMethod("string");
UtilClass.staticMethod("string");
verify(() -> UtilClass.staticMethod("string"), atLeastOnce());
verify(() -> UtilClass.staticMethod("string"), times(2));
verify(() -> UtilClass.staticVoidMethod("string"), atLeastOnce());
verify(() -> UtilClass.staticVoidMethod("string"), atMost(2));
} finally {
session.finishMocking();
}
}
verifyは () - > UtilClass.staticMethod()
のようにラムダ式で呼び出すメソッドを渡します。
そして atLeastOnce()
と同じ回数の情報を引数として渡します。
下APIは、予想されるメソッドの呼び出し回数を定義し、意味は次のとおりです。
- atLeastOnce():メソッドが最小一度呼び出されたことを確認
- times(wanted number):任意の数の呼び出しを確認
- atMost(max number):最大コール数、超過すると、テストに失敗し
- atLeast(min number):最小の呼び出し回数、未満の場合は、テストが失敗
この他にも atMost()
のような様々な回数のメソッドを提供します。
Reset mock
Mockingしたものを reset()
APIで初期化する例です。
@Test
public void resetMock() throws Exception {
MockitoSession session = mockitoSession().mockStatic(SuperClass.class).startMocking();
try {
assertNull(SuperClass.returnB());
when(SuperClass.returnB()).thenReturn("fakeB");
assertEquals("fakeB", SuperClass.returnB());
reset(staticMockMarker(SuperClass.class));
assertNull(SuperClass.returnB());
} finally {
session.finishMocking();
}
}
Androidのメソッドに適用
アンドロイドの Settings
のメソッドをspyingする例です。
@Test
public void spyStatic() throws Exception {
ContentResolver resolver = InstrumentationRegistry.getTargetContext().getContentResolver();
String deviceName = Settings.Global.getString(resolver, DEVICE_NAME);
MockitoSession session = mockitoSession().spyStatic(Settings.Global.class).startMocking();
try {
// Cannot call when(Settings.getString(any(ContentResolver.class), eq("...")))
// as any(ContentResolver.class) returns null which makes getString fail. Hence need to
// use less lambda API
doReturn("23").when(() -> Settings.Global.getString(any
(ContentResolver.class), eq("twenty three")));
doReturn(42).when(() -> Settings.Global.getInt(any
(ContentResolver.class), eq("fourty two")));
// Make sure behavior is changed
assertEquals("23", Settings.Global.getString(resolver, "twenty three"));
assertEquals(42, Settings.Global.getInt(resolver, "fourty two"));
// Make sure non-mocked methods work as before
assertEquals(deviceName, Settings.Global.getString(resolver, DEVICE_NAME));
} finally {
session.finishMocking();
}
}
サンプル
この記事で使用したサンプルは、GitHub - Sampleで確認することができます。
参考
Related Posts
- エラー解決:android gradle plugin requires java 11 to run. you are currently using java 1.8.
- Android - コルーチンとRetrofitによる非同期通信の例
- Android - コルーチンでURL画像を読み込む
- Android - 振動、Vibrator、VibrationEffectの例
- Some problems were found with the configuration of task
- Query method parameters should either be a type that can be converted into a database column or a List
- UbuntuでAndroid 12オープンソースをダウンロードしてビルド
- Android - ViewModelを生成する方法
- Android - Transformations.map(), switchMap() の違い
- Android-Transformations.distinctUntilChanged()소개
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - 携帯電話の電話番号を取得する方法
- Android 12 - Splash Screens
- Android 12 - インクリメンタルインストール
- Android - adbコマンドでbugreportログファイルの抽出
- Android - adbコマンドでAppデータを削除する
- Android - adbコマンドでアプリ無効化、有効化
- Android - adbコマンドで特定のパッケージのPIDを検索
- Android - adbコマンドでパーミッションGrantまたはRevoke
- Android - adbコマンドで特定のパッケージのプロセスの終了
- Android - adbコマンドでapkのインストール、削除、
- Android - adb push、pullでファイルのコピー、ダウンロード
- Android - adbコマンドでscreen capture保存
- Android - adbコマンドでSystemアプリの削除、インストール
- Android - adbコマンドでsettings value確認、変更、
- Android 12 - IntentFilterのexported明示的な宣言
- Android - adbコマンドで工場出荷時の(Factory reset)
- Android - adb logcatコマンドでログ出力
- Android - adbコマンドでメモリダンプ(dump-heap)
- Android - adbコマンドでApp強制終了(force-stop)
- Android - adbコマンドでServiceの実行、終了
- Android - adbコマンドでActivity実行
- Android - adbコマンドでBroadcast配信
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信