로그에서 방법 e를 모의하는 방법


81

여기서 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)

답변:


155

이것은 나를 위해 일했습니다. 저는 JUnit 만 사용하고 있으며 타사 라이브러리 없이도Log 클래스 매우 쉽게 모의 할 수있었습니다 . 내용이 있는 파일을 Log.java내부에 생성하십시오 app/src/test/java/android/util.

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

20
이것은 매우 훌륭합니다. 그리고 PowerMockito에 대한 필요성을 피합니다. 10/10
Sipty

1
좋은 대답, 내 이론은 단위 테스트에서 Mock API를 사용해야하는 경우 코드가 단위 테스트가 가능할만큼 충분히 구성되지 않은 것입니다. 외부 라이브러리를 사용하는 경우 런타임 및 실제 개체와 함께 통합 테스트를 사용하십시오. 내 모든 Android 앱에서 플래그를 기반으로 로그를 활성화하는 래퍼 클래스 LogUtil을 만들었습니다. 이렇게하면 Log 클래스를 조롱하지 않고 플래그를 사용하여 로그를 활성화 / 비활성화하는 데 도움이됩니다. 프로덕션에서는 어쨌든 progaurd로 모든 로그 문을 제거합니다.
MG Developer

4
@MGDevelopert 당신이 맞아요. IMO이 기술 / 트릭은 거의 사용되지 않아야합니다. 예를 들어, Log너무 편재하고 모든 곳에서 Log 래퍼를 전달하면 코드의 가독성이 떨어지기 때문에 클래스에 대해서만 그렇게합니다 . 대부분의 경우 종속성 주입을 대신 사용해야합니다.
Paglian

5
잘 작동합니다. 복사하여 붙여 넣기 직전에 패키지 이름을 추가합니다. package android.util;
Michał Dobi Dobrzański

1
@DavidKennedy 사용 @file:JvmName("Log")및 최상위 기능.
Miha_x64 2019

40

이것을 gradle 스크립트에 넣을 수 있습니다.

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

그러면 android.jar의 모의 해제 된 메서드가 예외를 throw할지 아니면 기본값을 반환할지 결정합니다.


26
문서에서 : 주의 : returnDefaultValues ​​속성을 true로 설정하는 것은 주의 해서 수행해야합니다. null / zero 반환 값은 테스트에서 회귀를 유발할 수 있으며, 이는 디버그하기 어렵고 실패한 테스트를 통과하도록 허용 할 수 있습니다. 최후의 수단으로 만 사용하십시오.
마니시 쿠마르 샤르마

31

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

멋진 소개 +1, 테스트를 통과했지만 이미 오류가보고되었습니다!
MHSFisher

1
Log.w를 캡처하려면 다음을 추가하십시오.every { Log.w(any(), any<String>()) } returns 0
MrK

Log.wtf( every { Log.wtf(any(), any<String>()) } returns 0) 와 ( 와 ) 함께 작동하지 않는 것 같습니다 Unresolved reference: wtf. 오류로 인해 컴파일이 실패합니다 : . IDE lint는 코드에서 아무것도 말하지 않습니다. 어떤 생각?
Mackovich

Brilliant +1 !! ... 이것은 Mockk를 사용하는 동안 나를 위해 일했습니다.
RKS

mockk를 Log.*사용 println()하여 의도 한 로그를 출력하는 데 사용할 호출을 만들 수 있습니까 ?
Emil S.

26

사용 PowerMockito를 :

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

그리고 당신은 갈 수 있습니다. PowerMockito는 상속 된 정적 메서드를 자동으로 모의하지 않으므로 Log를 확장하는 사용자 지정 로깅 클래스를 모의하려면 MyCustomLog.e ()와 같은 호출에 대해 여전히 Log를 모의해야합니다.


1
Gradle에서 PowerMockRunner 를 어떻게 얻었습니까 ??
IgorGanapolsky

4
@IgorGanapolsky 여기 내 대답을 참조 하십시오 .
plátano plomo

내 대답은 확인 코 틀린를 여기 Log.e 및 Log.println 조롱을 위해
바르 토스 Kosarzycki - kosiara

PowerMockito는 2019 년에도 Kotiln을위한 인기있는 솔루션입니까? 아니면 다른 모의 라이브러리 (예 : MockK)를 살펴 봐야합니다.
IgorGanapolsky

8

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

    }
 }

1
버그로 인해 JUnit 4.12의 경우 PowerMock> = 1.6.1을 사용합니다. 그렇지 않으면, JUnit을 함께 4.11 실행하려고
manasouza

5

PowerMock하나를 사용하면 Android 로거에서 Log.i / e / w 정적 메서드 를 모의 할 수 있습니다 . 물론 이상적으로 는 로깅 인터페이스 또는 파사드를 만들고 다른 소스에 로깅하는 방법을 제공해야합니다.

이것은 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")
    }
}

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방법 사용 을 모의하려면 :

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

Java에서도 가능합니까?
Bowi

@Bowi : 또한 JDK11의 작동 자바에서 System.out.println 내 솔루션 모의 Log.v를 참조 stackoverflow.com/a/63642300/3569768
Yingding 왕

4

나는 목재를 사용하는 것이 좋습니다로깅을 위해 를 .

테스트를 실행할 때 아무것도 기록하지 않지만 Android Log 클래스가 수행하는 방식처럼 불필요하게 테스트가 실패하지는 않습니다. Timber를 사용하면 앱의 디버그 및 프로덕션 빌드를 편리하게 제어 할 수 있습니다.


4

@Paglian 답변과 @ Miha_x64 댓글 덕분에 kotlin에서도 동일한 작업을 수행 할 수있었습니다.

다음 Log.kt 파일을 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...

그리고 Log.xxx에 대한 호출은 대신 이러한 함수를 호출해야합니다.


2

Mockito는 정적 메서드를 모의하지 않습니다. PowerMockito를 위에 사용하십시오. 여기 에 예가 있습니다.


1
@ user3762991 또한 매처를 변경해야합니다. thenReturn(...)문에 매처를 사용할 수 없습니다 . 유형의 값을 지정해야합니다. 자세한 정보는 여기에서 확인하세요
troig

e, d, v 메서드를 모의 할 수없는 경우이 제한 때문에 mockito를 사용할 수 없게됩니까?
user3762991

2
포크를 먹을 수 없다면 사용할 수 없게 되나요? 그것은 다른 목적을 가지고 있습니다.
Antiohia

1

또 다른 해결책은 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 ...
}

0

org.slf4j.Logger를 사용하는 경우 PowerMockito를 사용하여 테스트 클래스에서 Logger를 조롱하는 것이 저에게 효과적이었습니다.

@RunWith(PowerMockRunner.class)
public class MyClassTest {

@Mock
Logger mockedLOG;

...
}

0

에서 답 확장 kosiara을 사용하는 PowerMockMockito을 에서 자바JDK11 모의하기 android.Log.v와 방법 System.out.println안드로이드 스튜디오 4.0.1에서 테스트 유닛을.

이것은 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!");
    }

}

단위 테스트가있는 모듈 build.gradle 파일 에 종속성을 추가해야 합니다.

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}"
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.