Mockito - static、final methodをmockingする方法

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
    }
}
  1. mockingない場合は、実際のクラスで実装された値が返されます。
  2. MockitoSessionオブジェクトにクラスをmockingます。
  3. mockingが開始されると、メソッドはnullを返します。
  4. when、thenReturnを利用して、mockingを実装します。
  5. メソッドを呼び出したとき、設定した値が返されることを確認します。
  6. 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

codechachaCopyright ©2019 codechacha