लॉग में ई विधि को कैसे मॉक करें


81

यहाँ Utils.java मेरी कक्षा का परीक्षण किया जा रहा है और निम्नलिखित विधि है जिसे UtilsTest वर्ग में कहा जाता है। भले ही मैं नीचे दिए गए लॉग.ई पद्धति का मजाक उड़ा रहा हूं

 @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.javaapp/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
यह खूनी शानदार है। और यह पॉवरमॉकिटो की आवश्यकता को चकमा देता है। 10/10
सिप्टी

1
अच्छा उत्तर, मेरा सिद्धांत यह है कि यदि आपको यूनिट परीक्षण में मॉक एपीआई का उपयोग करना है तो आपका कोड इकाई परीक्षण योग्य होने के लिए पर्याप्त व्यवस्थित नहीं है। यदि आप बाहरी पुस्तकालयों का उपयोग कर रहे हैं तो एक रनटाइम और वास्तविक वस्तुओं के साथ एकीकरण परीक्षणों का उपयोग करें। मेरे सभी Android Apps में मैंने एक रैपर क्लास LogUtil बनाया है जो एक ध्वज के आधार पर लॉग को सक्षम करता है, इससे मुझे लॉग क्लास से बचने और लॉग के साथ लॉग को अक्षम / निष्क्रिय करने में मदद मिलती है। उत्पादन में मैं वैसे भी सभी लॉग स्टेटमेंट को प्रोगॉरड से हटा देता हूं।
एमजी डेवलपर

4
@MGDevelopert आप सही कह रहे हैं। IMO इस तकनीक / ट्रिक का उपयोग कम किया जाना चाहिए। उदाहरण के लिए, मैं केवल उस Logवर्ग के लिए करता हूं क्योंकि बहुत सर्वव्यापी है और हर जगह एक लॉग रैपर को पास करना कोड को कम पठनीय बनाता है। ज्यादातर मामलों में इसके बजाय निर्भरता इंजेक्शन का उपयोग किया जाना चाहिए।
पैग्लियन

5
अच्छा काम करता है। इससे पहले कि आप इसे कॉपी-पेस्ट करें, पैकेज का नाम जोड़ें: पैकेज android.util;
माइकल डोबी डोब्रज़ास्की

1
@DavidKennedy उपयोग @file:JvmName("Log")और शीर्ष-स्तरीय फ़ंक्शन।
Miha_x64

40

आप इसे अपनी श्रेणी स्क्रिप्ट में डाल सकते हैं:

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

यह तय करेगा कि android.jar से अनमॉक किए गए तरीके अपवाद छोड़ दें या डिफ़ॉल्ट मान लौटाएं।


26
डॉक्स से: सावधानी: रिटर्नडिफॉल्टलवाल्स प्रॉपर्टी को सच में सेट करना देखभाल के साथ किया जाना चाहिए। अशक्त / शून्य रिटर्न मान आपके परीक्षणों में प्रतिगमन प्रस्तुत कर सकते हैं, जो डीबग करना कठिन हैं और विफल परीक्षणों को पारित करने की अनुमति दे सकते हैं। केवल अंतिम उपाय के रूप में इसका उपयोग करें।
मनीष कुमार शर्मा

31

यदि कोटलिन का उपयोग करने के लिए मैं मॉकक जैसी आधुनिक लाइब्रेरी का उपयोग करने की सलाह दूंगा, जिसमें स्टैटिक्स और अन्य कई चीजों के लिए बिल्ट-इन हैंडलिंग हो। तो यह इस के साथ किया जा सकता है:

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 add को पकड़ना चाहते हैं:every { Log.w(any(), any<String>()) } returns 0
MrK

के साथ काम करने के लिए प्रतीत नहीं होता Log.wtf( every { Log.wtf(any(), any<String>()) } returns 0): त्रुटि के साथ संकलन faill Unresolved reference: wtf:। IDE लिंट कोड में कुछ भी नहीं कहता है। कोई उपाय ?
मैकॉविच

शानदार +1 !! ... Mockk का उपयोग करते समय इसने मेरे लिए काम किया।
आरकेएस

मैं कॉल करने के लिए बनाने के लिए mockk उपयोग कर सकते हैं Log.*का उपयोग println()उत्पादन करने का इरादा प्रवेश करें?
एमिल एस।

26

PowerMockito का उपयोग :

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

और आप जाने के लिए अच्छे हैं। यह सलाह दें कि PowerMockito स्वचालित रूप से विरासत में मिली स्थैतिक विधियों का मजाक नहीं उड़ाएगा, इसलिए यदि आप एक कस्टम लॉगिंग क्लास को मॉक करना चाहते हैं जो लॉग का विस्तार करता है, तो आपको अभी भी MyCustomLog.e () जैसे कॉल के लिए लॉग का मॉक करना होगा।


1
ग्रैडल में आपको पॉवरमॉकरनर कैसे मिला ??
इगोरगानापोलस्की

4
@IgorGanapolsky यहां मेरा जवाब देखें ।
प्लैटानो प्लोमो

में मेरा उत्तर की जाँच करें Kotlin यहाँ Log.e और Log.println मजाक के लिए
बार्तोज़ Kosarzycki - kosiara

क्या Kotiln के लिए 2019 में PowerMockito अभी भी एक लोकप्रिय समाधान है? या हमें अन्य नकली पुस्तकालयों (यानी मॉकके) को देखना चाहिए।
इगोरगानापोलस्की

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 स्थैतिक तरीकों का मजाक उड़ाया जा सकता है । बेशक आदर्श रूप से आपको एक लॉगिंग इंटरफ़ेस या एक मुखौटा बनाना चाहिए और विभिन्न स्रोतों से लॉगिंग का एक तरीका प्रदान करना चाहिए

यह कोटलिन में एक पूर्ण समाधान है:

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")
    }
}

ढाल में निर्भरता जोड़ने के लिए याद रखें:

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
}

क्या यह जावा में भी संभव है?
बोवी

@ बॉवी: जावा में system.out.println के साथ मेरा समाधान mock Log.v देखें। जो JDK11 stackoverflow.com/a/63642300/3569768 के
यिंगडिंग वांग

4

मैं आपके लॉगिंग के लिए लकड़ी का उपयोग करने की सलाह दूंगा।

हालांकि यह परीक्षण चलाने के दौरान कुछ भी लॉग नहीं करेगा, लेकिन यह आपके परीक्षणों को अनावश्यक रूप से विफल नहीं करता है जिस तरह से एंड्रॉइड लॉग क्लास करता है। टिम्बर आपको ऐप के डिबग और प्रोडक्शन बिल्ड दोनों पर बहुत सुविधाजनक नियंत्रण प्रदान करता है।


4

@Paglian जवाब और @ Miha_x64 टिप्पणी के लिए धन्यवाद, मैं कोटलिन के लिए एक ही काम करने में सक्षम था।

निम्नलिखित 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...

और voilà, Log.xxx के लिए आपके कॉल के बजाय theses फ़ंक्शन को कॉल करना चाहिए।


2

Mockito स्थैतिक तरीकों का मजाक नहीं उड़ाता है। शीर्ष पर PowerMockito का उपयोग करें। यहाँ एक उदाहरण है।


1
@ user3762991 इसके अलावा आपको अपने मैचर्स को बदलने की जरूरत है। आप thenReturn(...)कथन में किसी माचिस का उपयोग नहीं कर सकते । आपको एक ठोस मूल्य निर्दिष्ट करने की आवश्यकता है। यहाँ
ट्रिग

यदि ई, डी, वी विधि का मजाक नहीं उड़ाया जा सकता है, तो सिर्फ इस सीमा के कारण मॉकिटो अनुपयोगी हो जाता है?
user3762991

2
यदि आप एक कांटा नहीं खा सकते हैं, तो क्या यह अनुपयोगी हो जाता है? इसका बस एक और उद्देश्य है।
एंटियोहिया

1

एक अन्य उपाय रोबोइलेक्ट्रिक का उपयोग करना है। यदि आप इसे आज़माना चाहते हैं, तो इसके सेटअप की जाँच करें ।

अपने मॉड्यूल के build.gradle में, निम्नलिखित जोड़ें

testImplementation "org.robolectric:robolectric:3.8"

android {
  testOptions {
    unitTests {
      includeAndroidResources = true
    }
  }
}

और अपनी परीक्षा कक्षा में,

@RunWith(RobolectricTestRunner.class)
public class SandwichTest {
  @Before
  public void setUp() {
  }
}

रोबोइलेक्ट्रिक के नए संस्करणों (4.3 के साथ परीक्षण) में आपके परीक्षण वर्ग को निम्नानुसार देखना चाहिए:

@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowLog.class)
public class SandwichTest {
    @Before
    public void setUp() {
        ShadowLog.setupLogging();
    }

    // tests ...
}

0

यदि आप org.slf4j.Logger का उपयोग कर रहे हैं, तो पावरमॉकिटो का उपयोग करके टेस्ट क्लास में केवल लकड़हारा का मजाक उड़ाएं।

@RunWith(PowerMockRunner.class)
public class MyClassTest {

@Mock
Logger mockedLOG;

...
}

0

से जवाब का विस्तार kosiara प्रयोग करने के लिए PowerMock और Mockito में जावा के साथ JDK11 उपहास करने के लिए android.Log.vके साथ विधि System.out.printlnइकाई एंड्रॉयड स्टूडियो 4.0.1 में परीक्षण के लिए।

यह जावा में एक पूर्ण समाधान है:

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}"
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.