Aquí Utils.java es mi clase para ser probada y el siguiente es el método que se llama en la clase UtilsTest. Incluso si me estoy burlando del método Log.e como se muestra a continuación
@Before
public void setUp() {
when(Log.e(any(String.class),any(String.class))).thenReturn(any(Integer.class));
utils = spy(new Utils());
}
Obtengo la siguiente excepción
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
clase porque es demasiado ubicuo y pasar un contenedor de registro en todas partes hace que el código sea menos legible. En la mayoría de los casos, se debe utilizar la inyección de dependencia.@file:JvmName("Log")
y funciones de nivel superior.Puedes poner esto en tu script gradle:
android { ... testOptions { unitTests.returnDefaultValues = true } }
Eso decidirá si los métodos no modificados de android.jar deben generar excepciones o devolver valores predeterminados.
fuente
Si usa Kotlin, recomendaría usar una biblioteca moderna como mockk que tiene manejo incorporado para estática y muchas otras cosas. Entonces se puede hacer con esto:
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
fuente
every { Log.w(any(), any<String>()) } returns 0
Log.wtf
(every { Log.wtf(any(), any<String>()) } returns 0
): faills de compilación con el error:Unresolved reference: wtf
. IDE lint no dice nada en el código. Alguna idea ?Log.*
usarprintln()
para generar el registro deseado?Usando PowerMockito :
@RunWith(PowerMockRunner.class) @PrepareForTest({Log.class}) public class TestsToRun() { @Test public void test() { PowerMockito.mockStatic(Log.class); } }
Y estás listo para irte. Tenga en cuenta que PowerMockito no simulará automáticamente los métodos estáticos heredados, por lo que si desea simular una clase de registro personalizada que amplíe Log, debe simular Log para llamadas como MyCustomLog.e ().
fuente
Utilice 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(){ } }
fuente
El uso de
PowerMock
uno puede simular los métodos estáticos Log.i / e / w del registrador de Android. Por supuesto, lo ideal sería crear una interfaz de registro o una fachada y proporcionar una forma de registro a diferentes fuentes.Esta es una solución completa en 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") } }
recuerde agregar dependencias en 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' }
Para simular el
Log.println
uso del método:Mockito.`when`(Log.println(anyInt(), any(), any())).then { println(it.arguments[2] as String) 1 }
fuente
Recomendaría usar madera para su tala.
Aunque no registrará nada al ejecutar las pruebas, no fallará las pruebas innecesariamente como lo hace la clase de registro de Android. Timber le brinda un control conveniente sobre la depuración y la compilación de producción de su aplicación.
fuente
Gracias a la respuesta de @Paglian y al comentario de @ Miha_x64, pude hacer que lo mismo funcionara para kotlin.
Agregue el siguiente archivo Log.kt en
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...
Y voilà, tus llamadas a Log.xxx deberían llamar a estas funciones en su lugar.
fuente
Mockito no se burla de los métodos estáticos. Utilice PowerMockito en la parte superior. He aquí un ejemplo.
fuente
thenReturn(...)
declaración. Necesita especificar un valor tangible. Ver más información aquíOtra solución es utilizar Robolectric. Si quieres probarlo, comprueba su configuración .
En build.gradle de su módulo, agregue lo siguiente
testImplementation "org.robolectric:robolectric:3.8" android { testOptions { unitTests { includeAndroidResources = true } } }
Y en tu clase de prueba,
@RunWith(RobolectricTestRunner.class) public class SandwichTest { @Before public void setUp() { } }
En las versiones más recientes de Robolectric (probadas con 4.3) su clase de prueba debería tener el siguiente aspecto:
@RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowLog.class) public class SandwichTest { @Before public void setUp() { ShadowLog.setupLogging(); } // tests ... }
fuente
Si está usando org.slf4j.Logger, entonces simplemente burlarse del Logger en la clase de prueba usando PowerMockito funcionó para mí.
@RunWith(PowerMockRunner.class) public class MyClassTest { @Mock Logger mockedLOG; ... }
fuente
Ampliando la respuesta de kosiara para usar PowerMock y Mockito en Java con JDK11 para simular el
android.Log.v
método conSystem.out.println
pruebas unitarias en Android Studio 4.0.1.Esta es una solución completa en 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!"); } }
Recuerde agregar dependencias en el archivo build.gradle de su módulo donde existe su prueba unitaria:
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}" }
fuente