Как издеваться над методом e в журнале

Здесь 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)

Ответ 1

Это сработало для меня. Я использую только JUnit, и я смог легко объединить Log class без какой-либо третьей стороны lib. Просто создайте файл Log.java внутри app/src/test/java/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...
}

Ответ 2

Вы можете поместить это в свой gradle script:

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

Это решит, должны ли незафиксированные методы из android.jar генерировать исключения или возвращать значения по умолчанию.

Ответ 3

Использование PowerMockito:

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

И тебе хорошо идти. Имейте в виду, что PowerMockito не будет автоматически издеваться над унаследованными статическими методами, поэтому, если вы хотите издеваться над пользовательским классом ведения журнала, который расширяет Log, вы все равно должны издеваться над Log для таких вызовов, как MyCustomLog.e().

Ответ 4

Если вы используете 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

Ответ 5

Использовать 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(){

    }
 }

Ответ 6

Mockito не издевается над статическими методами. Используйте PowerMockito сверху. Здесь приведен пример.

Ответ 7

С помощью PowerMock можно издеваться над статическими методами Log.i/e/w из Android logger. Конечно, в идеале вы должны создать интерфейс регистрации или фасад и предоставить способ регистрации в различных источниках.

Это полное решение в Котлине:

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 метод Log.println используйте:

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

Ответ 8

Я бы порекомендовал использовать древесину для лесозаготовок.

Хотя он не будет ничего регистрировать при запуске тестов, но он не провалит ваши тесты без необходимости, как это делает класс Android-журнала. Timber дает вам удобный контроль как отладочной, так и производственной сборки вашего приложения.

Ответ 9

Другое решение - использовать 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 ...
}

Ответ 10

Если вы используете org.slf4j.Logger, тогда мне просто подшучивали над Logger в тестовом классе с помощью PowerMockito.

@RunWith(PowerMockRunner.class)
public class MyClassTest {

@Mock
Logger mockedLOG;

...
}