ログでメソッドeをモックする方法


81

ここで、Utils.javaはテスト対象のクラスであり、以下はUtilsTestクラスで呼び出されるメソッドです。以下に示すようにLog.eメソッドをモックしている場合でも

 @Before
  public void setUp() {
  when(Log.e(any(String.class),any(String.class))).thenReturn(any(Integer.class));
            utils = spy(new Utils());
  }

次の例外が発生します

java.lang.RuntimeException: Method e in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
    at android.util.Log.e(Log.java)
    at com.xxx.demo.utils.UtilsTest.setUp(UtilsTest.java:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

回答:


155

これは私にとってうまくいきました。私はJUnitのみを使用しており、サードパーティのライブラリなしでLogクラス非常に簡単にモックアップすることができました。内容を含むファイルをLog.java内部に作成するだけですapp/src/test/java/android/util

package android.util; 

public class Log {
    public static int d(String tag, String msg) {
        System.out.println("DEBUG: " + tag + ": " + msg);
        return 0;
    }

    public static int i(String tag, String msg) {
        System.out.println("INFO: " + tag + ": " + msg);
        return 0;
    }

    public static int w(String tag, String msg) {
        System.out.println("WARN: " + tag + ": " + msg);
        return 0;
    }

    public static int e(String tag, String msg) {
        System.out.println("ERROR: " + tag + ": " + msg);
        return 0;
    }

    // add other methods if required...
}

20
これは血まみれの素晴らしいです。そしてそれはPowerMockitoの必要性をかわします。10/10
Sipty 2017年

1
良い答え、私の理論は、ユニットテストでモックAPIを使用する必要がある場合、コードはユニットテストが可能なほど十分に編成されていません。外部ライブラリを使用している場合は、ランタイムおよび実オブジェクトとの統合テストを使用します。すべてのAndroidアプリで、フラグに基づいてログを有効にするラッパークラスLogUtilを作成しました。これにより、Logクラスのモックを回避し、フラグを使用してログを有効/無効にすることができます。本番環境では、とにかくprogaurdを使用してすべてのログステートメントを削除します。
MG Developer

4
@MGDevelopertその通りです。IMOこのテクニック/トリックはほとんど使用されるべきではありません。たとえば、私はLogクラスに対してのみこれを行います。これは、ユビキタスすぎて、どこにでもLogラッパーを渡すと、コードが読みにくくなるためです。ほとんどの場合、代わりに依存性注入を使用する必要があります。
パグリアン2018

5
うまく機能します。コピーして貼り付ける直前に、パッケージ名を追加します。packageandroid.util;
のMichałDobiDobrzański

1
@DavidKennedyの使用@file:JvmName("Log")とトップレベルの関数。
miha_x 6419

40

これをgradleスクリプトに入れることができます:

android {
   ...
   testOptions { 
       unitTests.returnDefaultValues = true
   }
}

これにより、android.jarのモックされていないメソッドが例外をスローするか、デフォルト値を返すかが決まります。


26
ドキュメントから: 注意: returnDefaultValuesプロパティをtrueに設定する場合は、注意が必要です。戻り値がnull /ゼロの場合、テストにリグレッションが発生する可能性があります。これはデバッグが難しく、失敗したテストに合格する可能性があります。最後の手段としてのみ使用してください。
マニッシュクマールシャルマ2017年

31

Kotlinを使用する場合は、静力学やその他の多くの処理が組み込まれているmockkのような最新のライブラリを使用することをお勧めします。次に、これで行うことができます:

mockkStatic(Log::class)
every { Log.v(any(), any()) } returns 0
every { Log.d(any(), any()) } returns 0
every { Log.i(any(), any()) } returns 0
every { Log.e(any(), any()) } returns 0

+1の導入は素晴らしいです。テストは合格ですが、エラーはすでに報告されています。
MHSFisher

1
Log.wをキャプチャする場合は、次を追加します。every { Log.w(any(), any<String>()) } returns 0
MrK20年

Log.wtfevery { Log.wtf(any(), any<String>()) } returns 0)で動作しないようです:コンパイルがエラーで失敗します:Unresolved reference: wtf。IDElintはコードで何も言いません。何か案が ?
Mackovich

ブリリアント+​​1 !! ...これは、Mockkを使用しているときにうまくいきました。
RKS

mockkをLog.*使用println()して、目的のログを出力するために使用する呼び出しを行うことはできますか?
エミール・S.

26

PowerMockitoの使用:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Log.class})
public class TestsToRun() {
    @Test
    public void test() {
        PowerMockito.mockStatic(Log.class);
    }
}

そして、あなたは行ってもいいです。PowerMockitoは継承された静的メソッドを自動的にモックしないことに注意してください。したがって、Logを拡張するカスタムロギングクラスをモックする場合でも、MyCustomLog.e()などの呼び出しのLogをモックする必要があります。


1
どのようにしてPowerMockRunnerをGradleで入手しましたか?
IgorGanapolsky 2016年

4
@IgorGanapolskyここで私の答えを参照してください
Platanoのplomo

Log.eとLog.printlnをあざけるためにここKotlin 私の答えをチェックしてください
kosiara – Bartosz Kosarzycki 2018

PowerMockitoは2019年もKotilnで人気のあるソリューションですか?または、他のモックライブラリ(つまりMockK)を調べる必要があります。
IgorGanapolsky

8

PowerMockitoを使用します。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassNameOnWhichTestsAreWritten.class , Log.class})
public class TestsOnClass() {
    @Before
    public void setup() {
        PowerMockito.mockStatic(Log.class);
    }
    @Test
    public void Test_1(){

    }
    @Test
    public void Test_2(){

    }
 }

1
バグのため、JUnit4.12ではPowerMock> = 1.6.1を使用することに注意してください。それ以外の場合は、JUnitの4.11で実行しよう
manasouza

5

PowerMock1つを使用すると、AndroidロガーからLog.i / e / w静的メソッドをモックできます。もちろん、理想的には、ロギングインターフェイスまたはファサード作成し、さまざまなソースにロギングする方法を提供する必要があります。

これはKotlinの完全なソリューションです。

import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

/**
 * Logger Unit tests
 */
@RunWith(PowerMockRunner::class)
@PrepareForTest(Log::class)
class McLogTest {

    @Before
    fun beforeTest() {
        PowerMockito.mockStatic(Log::class.java)
        Mockito.`when`(Log.i(any(), any())).then {
            println(it.arguments[1] as String)
            1
        }
    }

    @Test
    fun logInfo() {
        Log.i("TAG1,", "This is a samle info log content -> 123")
    }
}

gradleに依存関係を追加することを忘れないでください:

dependencies {
    testImplementation "junit:junit:4.12"
    testImplementation "org.mockito:mockito-core:2.15.0"
    testImplementation "io.kotlintest:kotlintest:2.0.7"
    testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.0-beta.5'
    testImplementation 'org.powermock:powermock-core:2.0.0-beta.5'
    testImplementation 'org.powermock:powermock-module-junit4:2.0.0-beta.5'
    testImplementation 'org.powermock:powermock-api-mockito2:2.0.0-beta.5'
}

Log.printlnメソッドの使用をモックするには:

Mockito.`when`(Log.println(anyInt(), any(), any())).then {
    println(it.arguments[2] as String)
    1
}

それはJavaでもどういうわけか可能ですか?
ボウイ

@Bowi:Javaでsystem.out.printlnを使用してLog.vをモックするソリューションを参照してください。これはJDK11でも機能しますstackoverflow.com/a/63642300/3569768
Wang

4

伐採には木材を使用することをお勧めします。

テストの実行時には何もログに記録されませんが、androidLogクラスのようにテストが不必要に失敗することはありません。Timberを使用すると、アプリのデバッグビルドと本番ビルドの両方を簡単に制御できます。


4

@Paglianの回答と@ Miha_x64のコメントのおかげで、kotlinでも同じことが機能するようになりました。

次のLog.ktファイルをに追加します app/src/test/java/android/util

@file:JvmName("Log")

package android.util

fun e(tag: String, msg: String, t: Throwable): Int {
    println("ERROR: $tag: $msg")
    return 0
}

fun e(tag: String, msg: String): Int {
    println("ERROR: $tag: $msg")
    return 0
}

fun w(tag: String, msg: String): Int {
    println("WARN: $tag: $msg")
    return 0
}

// add other functions if required...

そして、voilà、Log.xxxへの呼び出しは代わりにこれらの関数を呼び出す必要があります。


2

Mockitoは静的メソッドをモックしません。上部にPowerMockitoを使用します。これが例です。


1
@ user3762991また、マッチャーを変更する必要があります。thenReturn(...)ステートメントでマッチャーを使用することはできません。有形の値を指定する必要があります。詳細はこちら
troig 2016

e、d、vメソッドをモックできない場合、この制限のためにmockitoは使用できなくなりますか?
user3762991 2016

2
フォークが食べられなくなったら使えなくなりますか?それは単に別の目的を持っています。
アンティオキア2016

1

別の解決策は、Robolectricを使用することです。試してみたい場合は、設定を確認してください。

モジュールのbuild.gradleに、以下を追加します

testImplementation "org.robolectric:robolectric:3.8"

android {
  testOptions {
    unitTests {
      includeAndroidResources = true
    }
  }
}

そして、あなたのテストクラスでは、

@RunWith(RobolectricTestRunner.class)
public class SandwichTest {
  @Before
  public void setUp() {
  }
}

Robolectricの新しいバージョン(4.3でテスト済み)では、テストクラスは次のようになります。

@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowLog.class)
public class SandwichTest {
    @Before
    public void setUp() {
        ShadowLog.setupLogging();
    }

    // tests ...
}

0

org.slf4j.Loggerを使用している場合は、PowerMockitoを使用してテストクラスでロガーをモックするだけでうまくいきました。

@RunWith(PowerMockRunner.class)
public class MyClassTest {

@Mock
Logger mockedLOG;

...
}

0

以下からの回答を拡張kosiara使用するためのPowerMockMockitoをのJavaJDK11模擬しandroid.Log.vてメソッドをSystem.out.printlnAndroidのメーカー4.0.1でのユニットテストのために。

これはJavaの完全なソリューションです。

import android.util.Log;
import org.junit.Before;
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.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.mockito.ArgumentMatchers.any;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Log.class)
public class MyLogUnitTest {
    @Before
    public void setup() {
        // mock static Log.v call with System.out.println
        PowerMockito.mockStatic(Log.class);
        Mockito.when(Log.v(any(), any())).then(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                String TAG = (String) invocation.getArguments()[0];
                String msg = (String) invocation.getArguments()[1];
                System.out.println(String.format("V/%s: %s", TAG, msg));
                return null;
            }
        });
    }

    @Test
    public void logV() {
        Log.v("MainActivity", "onCreate() called!");
    }

}

単体テストが存在するモジュールbuild.gradleファイルに依存関係を追加することを忘れないでください。

dependencies {
    ...

    /* PowerMock android.Log for OpenJDK11 */
    def mockitoVersion =  "3.5.7"
    def powerMockVersion = "2.0.7"
    // optional libs -- Mockito framework
    testImplementation "org.mockito:mockito-core:${mockitoVersion}"
    // optional libs -- power mock
    testImplementation "org.powermock:powermock-module-junit4:${powerMockVersion}"
    testImplementation "org.powermock:powermock-api-mockito2:${powerMockVersion}"
    testImplementation "org.powermock:powermock-module-junit4-rule:${powerMockVersion}"
    testImplementation "org.powermock:powermock-module-junit4-ruleagent:${powerMockVersion}"
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.