Tutaj Utils.java jest moją klasą do przetestowania, a następującą metodą jest wywoływana w klasie UtilsTest. Nawet jeśli kpię z metody Log.e, jak pokazano poniżej
@Before
public void setUp() {
when(Log.e(any(String.class),any(String.class))).thenReturn(any(Integer.class));
utils = spy(new Utils());
}
Otrzymuję następujący wyjątek
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
klasy, ponieważ jest zbyt wszechobecne, a przekazywanie opakowania dziennika wszędzie sprawia, że kod jest mniej czytelny. W większości przypadków należy zamiast tego użyć iniekcji zależności.@file:JvmName("Log")
z funkcji najwyższego poziomu.Możesz umieścić to w swoim skrypcie gradle:
android { ... testOptions { unitTests.returnDefaultValues = true } }
To zadecyduje, czy niezmokowane metody z android.jar powinny zgłaszać wyjątki czy zwracać wartości domyślne.
źródło
Jeśli korzystasz z Kotlina, polecam korzystanie z nowoczesnej biblioteki, takiej jak mockk, która ma wbudowaną obsługę statyki i wielu innych rzeczy. Następnie można to zrobić za pomocą tego:
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
źródło
every { Log.w(any(), any<String>()) } returns 0
Log.wtf
(every { Log.wtf(any(), any<String>()) } returns 0
): faills kompilacji z powodu błędu:Unresolved reference: wtf
. Lint IDE nic nie mówi w kodzie. Dowolny pomysł ?Log.*
użyćprintln()
do wyprowadzenia przeznaczone Zaloguj?Korzystanie z PowerMockito :
@RunWith(PowerMockRunner.class) @PrepareForTest({Log.class}) public class TestsToRun() { @Test public void test() { PowerMockito.mockStatic(Log.class); } }
I jesteś gotowy. Należy pamiętać, że PowerMockito nie będzie automatycznie mockować dziedziczonych metod statycznych, więc jeśli chcesz mockować niestandardową klasę logowania, która rozszerza Log, nadal musisz mockować Log dla wywołań, takich jak MyCustomLog.e ().
źródło
Użyj 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(){ } }
źródło
Za pomocą
PowerMock
jednego można mockować metody statyczne Log.i / e / w z rejestratora systemu Android. Oczywiście najlepiej byłoby stworzyć interfejs logowania lub fasadę i zapewnić sposób logowania do różnych źródeł.To kompletne rozwiązanie w Kotlinie:
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") } }
pamiętaj, aby dodać zależności w 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' }
Aby kpić z
Log.println
metody użyj:Mockito.`when`(Log.println(anyInt(), any(), any())).then { println(it.arguments[2] as String) 1 }
źródło
Polecam drewno do wycinki.
Chociaż nie rejestruje niczego podczas uruchamiania testów, ale nie zawodzi ich niepotrzebnie, tak jak robi to klasa Android Log. Timber zapewnia wygodną kontrolę zarówno nad debugowaniem, jak i produkcją aplikacji.
źródło
Dzięki odpowiedzi @Paglian i komentarzowi @ Miha_x64 mogłem sprawić, że to samo zadziała dla kotlina.
Dodaj następujący plik Log.kt w
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...
I voilà, twoje połączenia z Log.xxx powinny zamiast tego wywoływać te funkcje.
źródło
Mockito nie kpi z metod statycznych. Użyj PowerMockito na górze. Oto przykład.
źródło
thenReturn(...)
instrukcji. Musisz określić namacalną wartość. Zobacz więcej informacji tutajInnym rozwiązaniem jest użycie Robolectric. Jeśli chcesz to wypróbować, sprawdź jego konfigurację .
W pliku build.gradle modułu dodaj następujące elementy
testImplementation "org.robolectric:robolectric:3.8" android { testOptions { unitTests { includeAndroidResources = true } } }
A w klasie testowej
@RunWith(RobolectricTestRunner.class) public class SandwichTest { @Before public void setUp() { } }
W nowszych wersjach Robolectric (testowanych z wersją 4.3) Twoja klasa testowa powinna wyglądać następująco:
@RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowLog.class) public class SandwichTest { @Before public void setUp() { ShadowLog.setupLogging(); } // tests ... }
źródło
Jeśli używasz org.slf4j.Logger, to po prostu mockowanie Loggera w klasie testowej przy użyciu PowerMockito zadziałało dla mnie.
@RunWith(PowerMockRunner.class) public class MyClassTest { @Mock Logger mockedLOG; ... }
źródło
Przedłużenie odpowiedzi od kosiary za używanie PowerMock i Mockito w Javie z JDK11 w celu mockowania
android.Log.v
metodySystem.out.println
do testów jednostkowych w Android Studio 4.0.1.To jest kompletne rozwiązanie w Javie:
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!"); } }
Pamiętaj, aby dodać zależności w pliku build.gradle modułu , w którym istnieje test jednostkowy:
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}" }
źródło