web-dev-qa-db-ja.com

Android単体テストして静的メソッドを模擬する方法

こんにちは私はあなたが私を助けることができることを本当に望みます、私は何日も私の髪を抜いてきたような気がします。

メソッドAの単体テストを記述しようとしています。メソッドAが静的メソッドBを呼び出しています。静的メソッドBをモックしたいのですが。

私はこれが以前に尋ねられたことを知っていますが、Androidはそれ以来成熟していると感じています。そして、テストしたいメソッドを書き直すことなく、そのような単純なタスクを実行する方法がなければなりません。

次に例を示します。最初にテストしたいメソッドです。

_public String getUserName(Context context, HelperUtils helper) {
    if(helper == null){
        helper = new HelperUtils();
    }
    int currentUserId = helper.fetchUsernameFromInternet(context);

    if (currentUserId == 1) {
        return "Bob";
    } else {
        return "Unknown";
    }
}
_

次に、モックしたい静的メソッド:

_public class HelperUtils {
    public static int fetchUsernameFromInternet(Context context) {
        int userid = 0;

        Log.i("HelperUtils ", "hello");

        return userid;
    }
}
_

他の言語ではこれはとても簡単ですが、Androidで機能させることはできません。 Mockitoを試しましたが、静的メソッドはサポートされていないようです

_HelperUtils helper = Mockito.mock(HelperUtils.class);
Mockito.when(helper.fetchUsernameFromInternet(getContext())).thenReturn(1);
_

このエラー

org.mockito.exceptions.misusing.MissingMethodInvocationException

Powermockを試しましたが、これがAndroidでサポートされているかどうか完全にはわかりません。私のgradleファイルでandroidCompileを使用してpowermockを実行することができましたが、このエラーが発生しました:

エラー:タスク ':app:dexDebugAndroidTest'の実行に失敗しました。 com.Android.ide.common.process.ProcessException:

言うまでもなく、PowerMockito.mockStatic(HelperUtils.class);は何も返さないため、getUsernameメソッドに何を渡すかわかりません。

どんな助けでもとてもいただければ幸いです。

13
James

静的メソッドはどのオブジェクトにも関連していません-helper.fetchUsernameFromInternet(...)HelperUtils.fetchUsernameFromInternet(...)と同じですが(少し混乱します)、このhelper.fetchUsernameFromInternetが原因でコンパイラ警告が表示されることもあります。

さらに、Mockito.mockの代わりに、使用する必要がある静的メソッドをモックします:@RunWith(...)@PrepareForTest(...)そしてPowerMockito.mockStatic(...)-完全な例はここにあります PowerMockito単一の静的メソッドをモックしてオブジェクトを返す

つまり、静的メソッド(およびコンストラクター)のモックは少し注意が必要です。より良い解決策は:

  • HelperUtilsを変更できる場合は、そのメソッドを非静的にして、通常のMockito.mockHelperUtilsをモックできます。

  • HelperUtilsを変更できない場合は、元のHelperUtilsに委譲するラッパークラスを作成しますが、staticメソッドがなく、通常のMockito.mock(これはアイデアは、「所有していないタイプをモックしないでください」と呼ばれることもあります)

8
iirekm

私はPowerMockitoを使用してこの方法を行いました。

使ってます AppUtils.class、複数の静的メソッドと関数が含まれています。

静的関数:

public static boolean isValidEmail(CharSequence target) {
    return target != null && EMAIL_PATTERN.matcher(target).matches();
}

テストケース:

@RunWith(PowerMockRunner.class)
@PrepareForTest({AppUtils.class})
public class AppUtilsTest {

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        PowerMockito.mockStatic(AppUtils.class);

        PowerMockito.when(AppUtils.isValidEmail(anyString())).thenCallRealMethod();
    }

    @Test
    public void testValidEmail() {
        assertTrue(AppUtils.isValidEmail("[email protected]"));
    }

    @Test
    public void testInvalidEmail1() {
        assertFalse(AppUtils.isValidEmail("[email protected]"));
    }

    @Test
    public void testInvalidEmail2() {
        assertFalse(AppUtils.isValidEmail("@email.com"));
    }
}

編集1:

次のインポートを追加します。

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

これがお役に立てば幸いです。

5
Hiren Patel