2009-07-06 19 views
162

मैं एक अमूर्त वर्ग का परीक्षण करना चाहता हूं। निश्चित रूप से, मैं कक्षा से विरासत में manually write a mock कर सकता हूं।अमूर्त कक्षाओं का परीक्षण करने के लिए मॉकिटो का उपयोग

क्या मैं अपने नकली हाथ-क्राफ्टिंग के बजाय एक मॉकिंग फ्रेमवर्क (मैं मॉकिटो का उपयोग कर रहा हूं) का उपयोग कर ऐसा कर सकता हूं? कैसे?

+2

मॉकिटो [1.10.12] (http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#30) के रूप में, मॉकिटो सीधे सार वर्गों को जासूसी/मजाक करने का समर्थन करता है: 'कुछ सार जासूस = जासूसी (SomeAbstract.class); ' – pesche

उत्तर

267

निम्नलिखित सुझाव है कि आप "असली" सबक्लास बनाने के बिना अमूर्त कक्षाओं का परीक्षण करें - मॉक उप-वर्ग है।

उपयोग Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS), तो कोई भी सार तरीकों कि लागू कर रहे हैं नकली।

उदाहरण:

public abstract class My { 
    public Result methodUnderTest() { ... } 
    protected abstract void methodIDontCareAbout(); 
} 

public class MyTest { 
    @Test 
    public void shouldFailOnNullIdentifiers() { 
     My my = Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS); 
     Assert.assertSomething(my.methodUnderTest()); 
    } 
} 

नोट: इस समाधान का सौंदर्य है कि आप सार तरीकों को लागू करने, जब तक कि वे कभी नहीं लागू नहीं कर रहे हैं है करते हैं।

अपनी ईमानदार राय में, यह एक जासूस का उपयोग कर से neater है, क्योंकि एक जासूस एक उदाहरण है, जिसका अर्थ है कि आप अपने अमूर्त वर्ग का instantiatable उपवर्ग बनाने के लिए की आवश्यकता है।

+0

+1 मुझे इस मुद्दे से संबंधित मेरे मुद्दे को हल करने में मदद के लिए +1। CALLS_REAL_METHODS मेरे SUT में स्टब ऑब्जेक्ट इंजेक्शन के लिए उपयोगी था। एक स्टब एक नकली से अधिक समझ में आया क्योंकि वापसी मूल्य थोड़ा जटिल थे। – Snekse

+10

जैसा कि नीचे बताया गया है, यह तब काम नहीं करता है जब अमूर्त वर्ग परीक्षण के लिए अमूर्त तरीकों को कॉल करता है, जो अक्सर होता है। –

+6

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

2

मान लिया जाये कि अपने परीक्षण वर्गों (एक अलग स्रोत जड़ से कम) एक ही पैकेज में कर रहे हैं के रूप में परीक्षण के अंतर्गत अपनी कक्षाओं आप बस नकली बना सकते हैं:

YourClass yourObject = mock(YourClass.class); 

और कॉल तरीकों तुम सिर्फ आप के रूप में परीक्षण करना चाहते हैं कोई अन्य विधि होगी।

आपको सुपर विधि को बुलाए जाने वाले किसी ठोस तरीके पर उम्मीद के साथ बुलाए जाने वाले प्रत्येक विधि के लिए अपेक्षाएं प्रदान करने की आवश्यकता है - यह सुनिश्चित नहीं है कि आप मॉकिटो के साथ ऐसा कैसे करेंगे, लेकिन मेरा मानना ​​है कि यह EasyMock के साथ संभव है।

यह सब कर रहा है YouClass का एक ठोस उदाहरण बना रहा है और आपको प्रत्येक सार विधि के खाली कार्यान्वयन प्रदान करने का प्रयास बचा रहा है।

एक तरफ, मुझे अक्सर अपने परीक्षण में अमूर्त वर्ग को लागू करने के लिए उपयोगी लगता है, जहां यह एक उदाहरण कार्यान्वयन के रूप में कार्य करता है जिसे मैं अपने सार्वजनिक इंटरफेस के माध्यम से परीक्षण करता हूं, हालांकि यह अमूर्त वर्ग द्वारा प्रदान की गई कार्यक्षमता पर निर्भर करता है।

+3

लेकिन नकली का उपयोग करना आपके क्लास के ठोस तरीकों का परीक्षण नहीं करेगा, या मैं गलत हूं? यह वही नहीं है जो मैं चाहता हूं। – ripper234

+1

यह सही है, अगर आप अमूर्त वर्ग पर ठोस तरीकों का आह्वान करना चाहते हैं तो उपर्युक्त काम नहीं करेगा। –

+0

क्षमाप्रार्थी, मैं उम्मीद के बारे में थोड़ा सा संपादित करूंगा, जो प्रत्येक विधि के लिए जरूरी है जिसे आप केवल अमूर्त लोगों को नहीं बुलाते हैं। –

13

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

एक सार वर्ग का परीक्षण करते समय, आप विषय अंडर टेस्ट (एसयूटी) के गैर-अमूर्त तरीकों को निष्पादित करना चाहते हैं, इसलिए एक मॉकिंग फ्रेमवर्क वह नहीं है जो आप चाहते हैं।

भ्रम का एक हिस्सा यह है कि आपके द्वारा लिंक किए गए प्रश्न का उत्तर हाथ से तैयार करने के लिए कहा जाता है जो आपके अमूर्त वर्ग से फैला हुआ है। मैं इस तरह के एक वर्ग को एक नकली नहीं कहूंगा। एक नकली एक वर्ग है जिसे निर्भरता के प्रतिस्थापन के रूप में उपयोग किया जाता है, उम्मीदों के साथ प्रोग्राम किया जाता है, और यह देखने के लिए पूछताछ की जा सकती है कि क्या उम्मीदें पूरी की जाती हैं या नहीं।

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

एक वैकल्पिक समाधान एसयूटी बनाने के लिए एक सार विधि के साथ अपने परीक्षण मामले को सार बनाना होगा (दूसरे शब्दों में, परीक्षण केस Template Method डिज़ाइन पैटर्न का उपयोग करेगा)।

6

कस्टम उत्तर का उपयोग करने का प्रयास करें।

उदाहरण के लिए:

import org.mockito.Mockito; 
import org.mockito.invocation.InvocationOnMock; 
import org.mockito.stubbing.Answer; 

public class CustomAnswer implements Answer<Object> { 

    public Object answer(InvocationOnMock invocation) throws Throwable { 

     Answer<Object> answer = null; 

     if (isAbstract(invocation.getMethod().getModifiers())) { 

      answer = Mockito.RETURNS_DEFAULTS; 

     } else { 

      answer = Mockito.CALLS_REAL_METHODS; 
     } 

     return answer.answer(invocation); 
    } 
} 

यह सार तरीकों के लिए नकली वापस आ जाएगी और ठोस तरीकों के लिए वास्तविक विधि कॉल करेंगे।

15

आप एक जासूस का उपयोग कर इसे प्राप्त कर सकते हैं (हालांकि मॉकिटो 1.8+ के नवीनतम संस्करण का उपयोग करें)।

public abstract class MyAbstract { 
    public String concrete() { 
    return abstractMethod(); 
    } 
    public abstract String abstractMethod(); 
} 

public class MyAbstractImpl extends MyAbstract { 
    public String abstractMethod() { 
    return null; 
    } 
} 

// your test code below 

MyAbstractImpl abstractImpl = spy(new MyAbstractImpl()); 
doReturn("Blah").when(abstractImpl).abstractMethod(); 
assertTrue("Blah".equals(abstractImpl.concrete())); 
5

क्या वास्तव में मुझे सार कक्षाएं मजाक के बारे में बुरा लग रहा है बनाता है, तथ्य यह है कि न तो डिफ़ॉल्ट निर्माता YourAbstractClass() कहा जाता हो जाता है (() नकली में सुपर लापता) और न ही वहाँ लगता है डिफ़ॉल्ट Mockito में किसी भी तरह से होना करने के लिए नकली गुणों को प्रारंभ करें (उदाहरण के लिए खाली ArrayList या LinkedList के साथ सूची गुण)।

मेरा सार वर्ग (मूल रूप से कक्षा स्रोत कोड उत्पन्न होता है) सूची तत्वों के लिए एक निर्भरता सेटर इंजेक्शन प्रदान नहीं करता है, न ही एक कन्स्ट्रक्टर जहां यह सूची तत्वों को प्रारंभ करता है (जिसे मैंने मैन्युअल रूप से जोड़ने का प्रयास किया)।

केवल वर्ग विशेषताओं का उपयोग डिफ़ॉल्ट प्रारंभ: निजी सूची dep1 = नए ArrayList; निजी सूची dep2 = नए ArrayList

तो कोई रास्ता नहीं एक वास्तविक वस्तु कार्यान्वयन (इकाई परीक्षण वर्ग में जैसे भीतरी वर्ग परिभाषा अधिभावी सार विधि) का उपयोग करते हुए और वास्तविक वस्तु (जो उचित करता है जासूसी के बिना एक अमूर्त वर्ग उपहास करने के लिए है क्षेत्र प्रारंभिक)।

बहुत बुरा है कि केवल PowerMock आगे की मदद करेगा।

66

तुम सिर्फ सार में से किसी को बिना छुए ठोस तरीकों के कुछ परीक्षण करने के लिए की जरूरत है, तो आप CALLS_REAL_METHODS (Morten's answer देखें), लेकिन परीक्षण के अंतर्गत ठोस विधि सार, या लागू नहीं इंटरफ़ेस तरीकों के कुछ कहता है, इसका उपयोग कर सकते काम नहीं करेगा - मॉकिटो शिकायत करेगा "जावा इंटरफ़ेस पर वास्तविक विधि को कॉल नहीं कर सकता।"

(हाँ, यह एक घटिया डिजाइन है, लेकिन कुछ चौखटे, जैसे टेपेस्ट्री 4, एक तरह से यह आप पर मजबूर करते हैं।)

वैकल्पिक हल इस दृष्टिकोण को उल्टा करने के लिए है - साधारण नकली व्यवहार का उपयोग (यानी, सब कुछ मज़ाक उड़ाया/टोंटदार) और doCallRealMethod() का उपयोग स्पष्ट रूप से परीक्षण के अंतर्गत ठोस विधि बाहर कॉल करने के लिए। जैसे

public abstract class MyClass { 
    @SomeDependencyInjectionOrSomething 
    public abstract MyDependency getDependency(); 

    public void myMethod() { 
     MyDependency dep = getDependency(); 
     dep.doSomething(); 
    } 
} 

public class MyClassTest { 
    @Test 
    public void myMethodDoesSomethingWithDependency() { 
     MyDependency theDependency = mock(MyDependency.class); 

     MyClass myInstance = mock(MyClass.class); 

     // can't do this with CALLS_REAL_METHODS 
     when(myInstance.getDependency()).thenReturn(theDependency); 

     doCallRealMethod().when(myInstance).myMethod(); 
     myInstance.myMethod(); 

     verify(theDependency, times(1)).doSomething(); 
    } 
} 

जोड़ने के लिए अपडेट किया गया:

गैर शून्य तरीकों के लिए, आप के बजाय thenCallRealMethod() का उपयोग करना होगा, जैसे:

when(myInstance.myNonVoidMethod(someArgument)).thenCallRealMethod(); 

अन्यथा Mockito "अधूरा शिकायत stubbing पता चला।"

+6

यह कुछ मामलों में काम करेगा, हालांकि मॉकिटो इस विधि के साथ अंतर्निहित अमूर्त वर्ग के निर्माता को कॉल नहीं करता है। यह एक अप्रत्याशित परिदृश्य के कारण "वास्तविक विधि" विफल हो सकता है। इस प्रकार, यह विधि सभी मामलों में भी काम नहीं करेगी। –

+2

हां, आप ऑब्जेक्ट की स्थिति पर बिल्कुल गिन सकते हैं, केवल विधि में कोड को बुलाया जा सकता है। –

1

आप अपने परीक्षण में एक गुमनाम वर्ग के साथ अमूर्त वर्ग का विस्तार कर सकते उदाहरण के लिए (का उपयोग कर JUnit 4):।

private AbstractClassName classToTest; 

@Before 
public void preTestSetup() 
{ 
    classToTest = new AbstractClassName() { }; 
} 

// Test the AbstractClassName methods. 
0

आप एक अनाम वर्ग का दृष्टांत अपने mocks इंजेक्षन और फिर उस वर्ग परीक्षण कर सकते हैं ।

@RunWith(MockitoJUnitRunner.class) 
public class ClassUnderTest_Test { 

    private ClassUnderTest classUnderTest; 

    @Mock 
    MyDependencyService myDependencyService; 

    @Before 
    public void setUp() throws Exception { 
     this.classUnderTest = getInstance(); 
    } 

    private ClassUnderTest getInstance() { 
     return new ClassUnderTest() { 

      private ClassUnderTest init(
        MyDependencyService myDependencyService 
      ) { 
       this.myDependencyService = myDependencyService; 
       return this; 
      } 

      @Override 
      protected void myMethodToTest() { 
       return super.myMethodToTest(); 
      } 
     }.init(myDependencyService); 
    } 
} 

ध्यान रखें कि दृश्यता सार वर्ग ClassUnderTest की संपत्ति myDependencyService के लिए protected होना चाहिए।

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

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