पैरामीटर के साथ एक निर्माणकर्ता को मॉक करें


89

मेरे पास नीचे के रूप में एक वर्ग है:

public class A {
    public A(String test) {
        bla bla bla
    }

    public String check() {
        bla bla bla
    }
}

कंस्ट्रक्टर में तर्क A(String test)और check()वे चीजें हैं जिनका मैं मजाक बनाने की कोशिश कर रहा हूं। मैं किसी भी कॉल की तरह चाहता हूं: new A($$$any string$$$).check()एक डमी स्ट्रिंग लौटाता है "test"

मैंने कोशिश की:

 A a = mock(A.class); 
 when(a.check()).thenReturn("test");

 String test = a.check(); // to this point, everything works. test shows as "tests"

 whenNew(A.class).withArguments(Matchers.anyString()).thenReturn(rk);
 // also tried:
 //whenNew(A.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(rk);

 new A("random string").check();  // this doesn't work

लेकिन यह काम नहीं कर रहा है। new A($$$any string$$$).check()अभी भी निर्माण तर्क के माध्यम से जा रहा है के बजाय नकली वस्तु लाने के लिए A


क्या आपका नकली चेक () विधि सही काम कर रहा है?
बेन ग्लासर

@BenGlasser चेक () ठीक काम करता है। बस जब बिल्कुल भी काम नहीं करता है। मैंने विवरण भी अपडेट किया।
शेंगजी

जवाबों:


93

आपके द्वारा पोस्ट किया गया कोड मॉकिटो और पॉवर्मॉकिटो के नवीनतम संस्करण के साथ मेरे लिए काम करता है। शायद आपने ए तैयार नहीं किया है? इसे इस्तेमाल करे:

A.java

public class A {
     private final String test;

    public A(String test) {
        this.test = test;
    }

    public String check() {
        return "checked " + this.test;
    }
}

MockA.java

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class MockA {
    @Test
    public void test_not_mocked() throws Throwable {
        assertThat(new A("random string").check(), equalTo("checked random string"));
    }
    @Test
    public void test_mocked() throws Throwable {
         A a = mock(A.class); 
         when(a.check()).thenReturn("test");
         PowerMockito.whenNew(A.class).withArguments(Mockito.anyString()).thenReturn(a);
         assertThat(new A("random string").check(), equalTo("test"));
    }
}

दोनों परीक्षणों को मॉकिटो 1.9.0, पॉवर्मॉकिटो 1.4.12 और जुनिट 4.8.2 के साथ पास होना चाहिए


24
यह भी ध्यान दें कि यदि निर्माणकर्ता को किसी अन्य वर्ग से बुलाया जाता है, तो इसे सूची में शामिल करेंPrepareForTest
जेफ ई

किसी को भी एक विचार है कि जब "PowerMockito.whenNew" कहा जाता है तो हमें स्वयं को क्यों तैयार करना चाहिए?
udayanga

50

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

पैटर्न 1 - ऑब्जेक्ट निर्माण के लिए एक-लाइन विधियों का उपयोग करना

पैटर्न 1 का उपयोग करने के लिए (MyClass नामक एक वर्ग का परीक्षण), आप कॉल की जगह लेंगे

   Foo foo = new Foo( a, b, c );

साथ में

   Foo foo = makeFoo( a, b, c );

और एक-पंक्ति विधि लिखें

   Foo makeFoo( A a, B b, C c ) { 
        return new Foo( a, b, c );
   }

यह महत्वपूर्ण है कि आप विधि में कोई तर्क शामिल न करें; सिर्फ एक लाइन जो वस्तु का निर्माण करती है। इसका कारण यह है कि विधि कभी भी इकाई परीक्षण करने वाली नहीं है।

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

आपके परीक्षण वर्ग में सदस्य जैसे हो सकते हैं

  @Mock private Foo mockFoo;
  private MyClass toTest = spy(new MyClass());

अंत में, अपने परीक्षण पद्धति के अंदर आप कॉल करना चाहते हैं जैसे कि लाइन के साथ मेकअप करें

  doReturn( mockFoo )
      .when( toTest )
      .makeFoo( any( A.class ), any( B.class ), any( C.class ));

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

यदि आप अपनी कक्षा की एक नकली वस्तु वापस करना चाहते हैं तो मुझे लगता है कि यह आपके लिए काम करना चाहिए। किसी भी मामले में आप यहां पर ऑब्जेक्ट के निर्माण के बारे में अधिक पढ़ सकते हैं:

http://code.google.com/p/mockito/wiki/MockingObjectCreation


21
+1, मुझे यह तथ्य पसंद नहीं है कि मुझे अपने स्रोत कोड को समायोजित करने की आवश्यकता है जो इसे अधिक मॉकिटो के अनुकूल बनाता है। साझा करने के लिए धन्यवाद।
शेंग्जी

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

1
टेस्टेबल कोड लिखना अच्छा है। क्लास ए को फिर से डिज़ाइन करने के लिए मजबूर किया जा रहा है ताकि मैं क्लास बी के लिए टेस्ट लिख सकूं, जो ए पर निर्भर करता है, क्योंकि ए में सी पर एक कठिन-कोडित निर्भरता है, लगता है ... कम अच्छा। हाँ, कोड अंत में बेहतर होगा, लेकिन मैं कितने वर्गों को फिर से डिज़ाइन करूंगा ताकि मैं एक परीक्षण लिखना समाप्त कर सकूं?
मार्क वुड

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

12

पावरमॉक का उपयोग किए बिना .... बेन ग्लासर जवाब के आधार पर नीचे दिए गए उदाहरण को देखें क्योंकि मुझे इसे समझने में कुछ समय लगा था .. जो कि कई बार बचाता है ...

मूल वर्ग:

public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(new BClazz(cClazzObj, 10));
    } 
}

संशोधित वर्ग:

@Slf4j
public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(getBObject(cClazzObj, 10));
    }

    protected BClazz getBObject(CClazz cClazzObj, int i) {
        return new BClazz(cClazzObj, 10);
    }
 }

टेस्ट क्लास

public class AClazzTest {

    @InjectMocks
    @Spy
    private AClazz aClazzObj;

    @Mock
    private CClazz cClazzObj;

    @Mock
    private BClazz bClassObj;

    @Before
    public void setUp() throws Exception {
        Mockito.doReturn(bClassObj)
               .when(aClazzObj)
               .getBObject(Mockito.eq(cClazzObj), Mockito.anyInt());
    }

    @Test
    public void testConfigStrategy() {
        aClazzObj.updateObject(cClazzObj);

        Mockito.verify(cClazzObj, Mockito.times(1)).setBundler(bClassObj);
    }
}

6

मॉकिटो के साथ आप विस्सेटिंग () का उपयोग कर सकते हैं, उदाहरण के लिए यदि काउंटरसेवर को 2 निर्भरता की आवश्यकता होती है, तो आप उन्हें मॉक के रूप में पास कर सकते हैं:

UserService userService = Mockito.mock(UserService.class); SearchService searchService = Mockito.mock(SearchService.class); CounterService counterService = Mockito.mock(CounterService.class, withSettings().useConstructor(userService, searchService));


मेरी राय में, सबसे आसान और सबसे अच्छा जवाब। धन्यवाद।
एल्डन

4

मॉकिटो की अंतिम, स्थिर और निजी विधियों का परीक्षण करने की सीमाएँ हैं।

jMockit परीक्षण पुस्तकालय के साथ, आप नीचे के रूप में कुछ सामान बहुत आसान और सीधे आगे कर सकते हैं:

एक java.io.File वर्ग का नकली निर्माण:

new MockUp<File>(){
    @Mock
    public void $init(String pathname){
        System.out.println(pathname);
        // or do whatever you want
    }
};
  • सार्वजनिक निर्माण का नाम $ init से बदला जाना चाहिए
  • तर्क और अपवाद को फेंक दिया गया है
  • वापसी प्रकार को शून्य के रूप में परिभाषित किया जाना चाहिए

एक स्थैतिक विधि का मजाक उड़ाएं:

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