अमूर्त वर्गों का परीक्षण करने के लिए मॉकिटो का उपयोग करना


213

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

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


2
Mockito के रूप में 1.10.12 , Mockito का समर्थन करता है जासूसी / सार कक्षाएं मजाक सीधे:SomeAbstract spy = spy(SomeAbstract.class);
Pesche

6
मॉकिटो 2.7.14 के अनुसार, आप अमूर्त वर्गीयता का भी मज़ाक उड़ा सकते हैं, जिसके लिए रचनाकार तर्कों की आवश्यकता होती हैmock(MyAbstractClass.class, withSettings().useConstructor(arg1, arg2).defaultAnswer(CALLS_REAL_METHODS))
Gediminas Rimsa

जवाबों:


315

निम्नलिखित सुझाव दें यदि आप एक 'असली' उपवर्ग बनाए बिना सार वर्गों का परीक्षण है - नकली है उपवर्ग।

उपयोग करें 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());
    }
}

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

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


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

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

2
मैं इस तरह की वस्तु (वास्तविक तरीकों को कॉल करने वाले अमूर्त वर्ग) पर निर्भरता कैसे इंजेक्ट कर सकता हूं?
शमूएल

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

1
अगर सार वर्ग निर्माता एक या अधिक पैरामीटर लेता है तो क्या होगा?
एसडी

68

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

(हाँ, यह एक घटिया डिजाइन है, लेकिन कुछ चौखटे, उदाहरण के लिए टेपेस्ट्री 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();

अन्यथा मॉकिटो शिकायत करेगा "अनफिनिश्ड स्टबिंग का पता चला।"


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

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

ओह, तो वस्तु विधियां राज्य से अलग हो जाती हैं, महान।
1948 में हेलिक्स

17

आप एक जासूस का उपयोग करके इसे प्राप्त कर सकते हैं (हालांकि मॉकिटो 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()));

14

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

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

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

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

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


8

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

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

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

यह अमूर्त विधियों के लिए नकली लौटाएगा और ठोस तरीकों के लिए वास्तविक विधि को कॉल करेगा।


5

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

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

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

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

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


2

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

YourClass yourObject = mock(YourClass.class);

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

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

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

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


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

1
यह सही है, उपरोक्त कार्य नहीं करेगा यदि आप अमूर्त वर्ग पर ठोस तरीकों को लागू करना चाहते हैं।
रिचर्ड निकोल्स

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

लेकिन फिर भी आप अपने मॉक का परीक्षण कर रहे हैं, ठोस तरीकों का नहीं।
जोनाटन क्लाउटियर

2

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

private AbstractClassName classToTest;

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

// Test the AbstractClassName methods.

2

मॉकिटो @Mockएनोटेशन के माध्यम से अमूर्त वर्गों का मजाक उड़ाने की अनुमति देता है :

public abstract class My {

    public abstract boolean myAbstractMethod();

    public void myNonAbstractMethod() {
        // ...
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyTest {

    @Mock(answer = Answers.CALLS_REAL_METHODS)
    private My my;

    @Test
    private void shouldPass() {
        BDDMockito.given(my.myAbstractMethod()).willReturn(true);
        my.myNonAbstractMethod();
        // ...
    }
}

नुकसान यह है कि इसका उपयोग आपको निर्माण मापदंडों की आवश्यकता नहीं है।


0

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

@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);
    }
}

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


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