2015-08-26 5 views
8

कक्षा मैं परीक्षण करना चाहते हैं:मैंने डूटरर्न का इस्तेमाल किया, मॉकिटो अभी भी अज्ञात वर्ग के अंदर वास्तविक कार्यान्वयन क्यों कहेंगे?

import com.google.common.cache.CacheBuilder; 
import com.google.common.cache.CacheLoader; 
import com.google.common.cache.LoadingCache; 

public class Subject { 

    private CacheLoader<String, String> cacheLoader = new CacheLoader<String, String>() { 
     @Override 
     public String load(String key) 
       throws Exception { 
      return retrieveValue(key); 
     } 
    }; 

    private LoadingCache<String, String> cache = CacheBuilder.newBuilder() 
      .build(cacheLoader); 

    public String getValue(String key) { 
     return cache.getUnchecked(key); 
    } 

    String retrieveValue(String key) { 
     System.out.println("I should not be called!"); 
     return "bad"; 
    } 
} 

यहाँ मेरी परीक्षण का मामला

import static org.junit.Assert.assertEquals; 
import static org.mockito.Matchers.anyString; 
import static org.mockito.Mockito.doReturn; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.InjectMocks; 
import org.mockito.Spy; 
import org.mockito.runners.MockitoJUnitRunner; 

@RunWith(MockitoJUnitRunner.class) 
public class SubjectTest { 

    String good = "good"; 

    @Spy 
    @InjectMocks 
    private Subject subject; 

    @Test 
    public void test() { 
     doReturn(good).when(subject).retrieveValue(anyString()); 
     assertEquals(good, subject.getValue("a")); 
    } 
} 

मैं

org.junit.ComparisonFailure: 
Expected :good 
Actual :bad 
+2

क्या आप सार्वजनिक विधि को 'पुनर्प्राप्त कर सकते हैं' और देख सकते हैं कि यह काम करता है या नहीं? – KDM

+0

@ केडीएम अभी भी काम नहीं कर रहा है। सुझाव – ssgao

उत्तर

6

मार्क पीटर्स ने मूल कारण का निदान और व्याख्या करने का एक अच्छा काम किया। एक अलग विधि में

  • ले जाएँ कैश (पुनः) प्रारंभ: मैं एक जोड़ी समाधान के बारे में सोच सकते हैं।

    जासूसी के भीतर से new CacheLoader पर कॉल करके, गुमनाम आंतरिक वर्ग को जासूस के संदर्भ के साथ मूल उदाहरण के रूप में बनाया गया है। परीक्षण के तहत आपकी वास्तविक प्रणाली के आधार पर, आप कन्स्ट्रक्टर पथ से कैश बनाने से लाभ प्राप्त कर सकते हैं, खासकर यदि कोई भारी प्रारंभिकरण या लोडिंग शामिल है।

    public class Subject { 
    
        public Subject() { 
        initializeCache(); 
        } 
    
        private LoadingCache<String, String> cache; 
    
        @VisibleForTesting 
        void initializeCache() { 
        cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() { 
         @Override 
         public String load(String key) throws Exception { 
         return retrieveValue(key); 
         } 
        }); 
        } 
    
        /* ... */ 
    } 
    
    @Test 
    public void test() { 
        subject.initializeCache(); 
        doReturn(good).when(subject).retrieveValue(anyString()); 
        assertEquals(good, subject.getValue("a")); 
    } 
    
  • एक मैनुअल ओवरराइड करें।

    आपकी परेशानी का मूल कारण यह है कि जासूस उदाहरण मूल उदाहरण से अलग है। अपने परीक्षण में एक उदाहरण को ओवरराइड करके, आप विसंगति से निपटने के बिना व्यवहार बदल सकते हैं।

    @Test 
    public void test() { 
        Subject subject = new Subject() { 
        @Override public String getValue() { return "good"; } 
        } 
    } 
    
  • Refactor।

    हालांकि आप पूर्ण डि लिए जा सकते हैं, तो आप सिर्फ मूल्य कार्य करने के लिए एक परीक्षण सीवन जोड़ने में सक्षम हो सकता है:

    public class Subject { 
    
        private CacheLoader<String, String> cacheLoader = new CacheLoader<String, String>() { 
        @Override 
        public String load(String key) throws Exception { 
         return valueRetriever.apply(key); 
        } 
        }; 
    
        private LoadingCache<String, String> cache = 
         CacheBuilder.newBuilder().build(cacheLoader); 
    
        Function<String, String> valueRetriever = new Function<String, String>() { 
        @Override 
        public String apply(String t) { 
         System.out.println("I should not be called!"); 
         return "bad"; 
        } 
        }; 
    
        public String getValue(String key) { 
        return cache.getUnchecked(key); 
        } 
    } 
    
    @Test 
    public void test() { 
        subject = new Subject(); 
        subject.valueRetriever = (x -> good); 
        assertEquals(good, subject.getValue("a")); 
    } 
    

    स्वाभाविक रूप से, अपनी आवश्यकताओं के आधार पर, valueRetriever एक पूरी तरह से अलग वर्ग हो सकता है, या आप एक पैरामीटर के रूप में एक संपूर्ण CacheLoader स्वीकार कर सकते हैं।

7

मिला इस जासूस के कार्यान्वयन के लिए नीचे आता है। docs के अनुसार, जासूस के रूप में एक वास्तविक उदाहरण के कॉपी बनाई गई है:

Mockito नहीं प्रतिनिधि पारित कर दिया वास्तविक उदाहरण के लिए कॉल करता है, बजाय यह वास्तव में यह एक कॉपी बन जाती। इसलिए यदि आप वास्तविक उदाहरण रखते हैं और इसके साथ बातचीत करते हैं, तो उम्मीद न करें कि जासूसी उन बातचीत और असली घटना राज्य पर उनके प्रभाव से अवगत रहें। अनुशासनिक यह है कि जब unstubbed विधि पर जासूसी पर पर वास्तविक उदाहरण पर नहीं है, तो आपको वास्तविक उदाहरण पर कोई प्रभाव नहीं दिखाई देगा।

ऐसा लगता है कि यह उथले प्रतिलिपि है। नतीजतन, जहां तक ​​मेरा डीबगिंग दिखाता है, CacheLoader प्रतिलिपि और मूल वस्तु के बीच साझा किया जाता है, लेकिन इसकी संलग्न वस्तु का संदर्भ मूल वस्तु है, न कि जासूसी। इसलिए वास्तविक retrieveValue को मजाक के बजाय बुलाया जाता है।

मुझे यकीन नहीं है कि इसे हल करने का सबसे अच्छा तरीका क्या होगा। इस विशिष्ट उदाहरण के लिए एक तरीका CacheLoader निर्भरता को उलटा करना होगा (यानी इसे Subject में Subject के बजाय इसे आंतरिक रूप से परिभाषित करना होगा), और Subject के बजाय नकली करें।

+0

के लिए धन्यवाद बहुत बहुत धन्यवाद। मैं वर्तमान में इस तथ्य को स्वीकार करने की कोशिश कर रहा हूं कि मैं पूरी विधि को नकल करने के लिए _doReturn() _ का उपयोग नहीं कर सकता, और इसके बजाय _retriveValue() _ विधि के अंदर मेरे पास नकली चीजें हैं। मैं अभी भी देखना चाहता हूं कि कोई इस काम को करने के लिए एक स्मार्ट समाधान के साथ आ सकता है या नहीं। धन्यवाद। – ssgao

+0

@ssgao: यदि आप इसे चूक गए हैं, तो मैंने एक मार्ग का सुझाव दिया था जिसमें 'getValue' (निर्भरता इंजेक्शन का उपयोग करने के लिए, जो लगभग हमेशा मॉकिंग के साथ संयोजन में उपयोग किया जाता है) का मज़ाक उड़ाता नहीं था। मुझे लगता है कि आपको "स्मार्ट" समाधान नहीं मिल रहा है क्योंकि आंशिक मॉकिंग आमतौर पर एक डिजाइन गंध है। पहले अपनी इकाई निर्भरताओं को तोड़ना बेहतर है। मॉकिटो के पास एक मानक उपदेश है: http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#16 –

संबंधित मुद्दे