How to mock method e in Log

AndroidJunitMockito

Android Problem Overview


Here Utils.java is my class to be tested and following is the method which is called in UtilsTest class. Even if I am mocking Log.e method as shown below

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

I am getting the following exception

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)

Android Solutions


Solution 1 - Android

This worked out for me. I'm only using JUnit and I was able to mock up the Log class without any third party lib very easy. Just create a file Log.java inside app/src/test/java/android/util with contents:

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...
}

Solution 2 - Android

You can put this into your gradle script:

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

That will decide whether unmocked methods from android.jar should throw exceptions or return default values.

Solution 3 - Android

If using Kotlin I would recommend using a modern library like mockk which has built-in handling for statics and many other things. Then it can be done with this:

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

Solution 4 - Android

Using PowerMockito:

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

And you're good to go. Be advised that PowerMockito will not automatically mock inherited static methods, so if you want to mock a custom logging class that extends Log, you must still mock Log for calls such as MyCustomLog.e().

Solution 5 - Android

Thanks to @Paglian answer and @Miha_x64 comment, I was able to make the same thing work for kotlin.

Add the following Log.kt file in 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...

And voilĂ , your calls to Log.xxx should call theses functions instead.

Solution 6 - Android

Use 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(){
        
    }
 }

Solution 7 - Android

Using PowerMock one can mock Log.i/e/w static methods from Android logger. Of course ideally you should create a logging interface or a facade and provide a way of logging to different sources.

This is a complete solution in 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")
    }
}

remember to add dependencies in 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'
}

To mock Log.println method use:

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

Solution 8 - Android

I would recommend using timber for your logging.

Though it will not log anything when running tests but it doesn't fail your tests unnecessarily the way android Log class does. Timber gives you a lot of convenient control over both debug and production build of you app.

Solution 9 - Android

Another solution is to use Robolectric. If you want to try it, check its setup.

In your module's build.gradle, add the following

testImplementation "org.robolectric:robolectric:3.8"

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

And in your test class,

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

In newer versions of Robolectric (tested with 4.3) your test class should look as follows:

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

    // tests ...
}

Solution 10 - Android

The kotlin version of @Paglian 's answer, no need to mock android.util.Log for JUnit tests :)

Emphasis:

1 -> the package name at the top

2 -> the annotation on top of the functions

package android.util

class Log {
    companion object {
        fun d(tag: String, msg: String): Int {
            println("DEBUG: $tag: $msg")
            return 0
        }

        @JvmStatic
        fun i(tag: String, msg: String): Int {
            println("INFO: $tag: $msg")
            return 0
        }

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

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

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

Solution 11 - Android

Mockito doesn't mock static methods. Use PowerMockito on top. Here is an example.

Solution 12 - Android

If your are using the org.slf4j.Logger, then just mocking the Logger in test class using PowerMockito worked for me.

@RunWith(PowerMockRunner.class)
public class MyClassTest {

@Mock
Logger mockedLOG;

...
}

Solution 13 - Android

Extending the answer from kosiara for using PowerMock and Mockito in Java with JDK11 to mock the android.Log.v method with System.out.println for unit testing in Android Studio 4.0.1.

This is a complete solution in 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!");
    }

}

Remember to add dependencies in your module build.gradle file where your unit test exists:

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}"
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser3762991View Question on Stackoverflow
Solution 1 - AndroidPaglianView Answer on Stackoverflow
Solution 2 - AndroidIgorGanapolskyView Answer on Stackoverflow
Solution 3 - AndroidGreg EnnisView Answer on Stackoverflow
Solution 4 - Androidplátano plomoView Answer on Stackoverflow
Solution 5 - AndroidAbelView Answer on Stackoverflow
Solution 6 - AndroidPayal KothariView Answer on Stackoverflow
Solution 7 - Androidkosiara - Bartosz KosarzyckiView Answer on Stackoverflow
Solution 8 - AndroidTosin JohnView Answer on Stackoverflow
Solution 9 - AndroidLipiView Answer on Stackoverflow
Solution 10 - AndroidHenriqueMSView Answer on Stackoverflow
Solution 11 - AndroidAntiohiaView Answer on Stackoverflow
Solution 12 - Androiduser0904View Answer on Stackoverflow
Solution 13 - AndroidYingding WangView Answer on Stackoverflow