Hier ist Utils.java meine zu testende Klasse und die folgende Methode wird in der UtilsTest-Klasse aufgerufen. Auch wenn ich mich über die unten gezeigte Log.e-Methode lustig mache
@Before
public void setUp() {
when(Log.e(any(String.class),any(String.class))).thenReturn(any(Integer.class));
utils = spy(new Utils());
}
Ich erhalte die folgende Ausnahme
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)
Log
Klasse, weil es zu allgegenwärtig ist und das Übergeben eines Log-Wrappers überall den Code weniger lesbar macht. In den meisten Fällen sollte stattdessen die Abhängigkeitsinjektion verwendet werden.@file:JvmName("Log")
und Funktionen der obersten Ebene.Sie können dies in Ihr Gradle-Skript einfügen:
android { ... testOptions { unitTests.returnDefaultValues = true } }
Dadurch wird entschieden, ob nicht gesockelte Methoden aus android.jar Ausnahmen auslösen oder Standardwerte zurückgeben sollen.
quelle
Wenn Sie Kotlin verwenden, würde ich die Verwendung einer modernen Bibliothek wie mockk empfehlen, die über eine integrierte Handhabung für Statik und viele andere Dinge verfügt. Dann kann es damit gemacht werden:
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
quelle
every { Log.w(any(), any<String>()) } returns 0
Log.wtf
(every { Log.wtf(any(), any<String>()) } returns 0
): compilation faills with error:Unresolved reference: wtf
. IDE lint says nothing in code. Any idea ?Log.*
useprintln()
to output the intended Log?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().
quelle
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(){ } }
quelle
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 }
quelle
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.
quelle
Mockito doesn't mock static methods. Use PowerMockito on top. Here is an example.
quelle
thenReturn(...)
statement. You need to specify a tangible value. See more info hereThanks 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.
quelle
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 ... }
quelle
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; ... }
quelle
Extending the answer from kosiara for using PowerMock and Mockito in Java with JDK11 to mock the
android.Log.v
method withSystem.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}" }
quelle