2011-12-22 15 views
10

इसलिए मुझे हमारी विकास टीम के लिए मॉकिंग और बीडीडी पर पढ़ने के लिए कहा गया है और हमारे मौजूदा यूनिट परीक्षणों (एक प्रयोग के रूप में) के मुकाबले में सुधार करने के लिए मैक्स के साथ खेलना है।मॉकिटो: "ब्लैकबॉक्स" निर्भरता का मजाक

मैंने आखिरकार मॉकिटो के साथ कई कारणों से (मेरे नियंत्रण के दायरे से बाहर) के लिए जाने का चयन किया है, लेकिन अर्थात् क्योंकि यह मॉकिंग के दौरान उदाहरण के लिए स्टबिंग और मॉकिंग दोनों का समर्थन करता है।

मैंने पूरे दिन मॉकिटो के बारे में सीखने, मजाक करने (सामान्य रूप से) और बीडीडी के बारे में सीखने में बिताया है। और अब मैं खोदने और हमारे यूनिट परीक्षणों को बढ़ाने के लिए तैयार हूं।

public class WebAdaptor { 

    private Subscriber subscriber; 

    public void run() { 

     subscriber = new Subscriber(); 
     subscriber.init(); 
    } 
} 

कृपया ध्यान दें:

तो हम एक वर्ग WebAdaptor नामक एक run() विधि है कि है (इस सवाल के दायरे से बाहर कारणों के लिए) मैं इस कोड को संशोधित करने के लिए एक रास्ता नहीं है । इस प्रकार मैं में Subscriber के लिए एक सेटर विधि जोड़ने की क्षमता है, और इस प्रकार इसे मेरे WebAdaptor के अंदर एक पहुंचने योग्य "ब्लैकबॉक्स" के रूप में सोचा जा सकता है।

मैं एक इकाई परीक्षण जो एक Mockito नकली को शामिल किया गया, और कहा कि नकली करने के लिए verify कि क्रियान्वित WebAdaptor::run() का कारण बनता है Subscriber::init() के नाम से जाना का उपयोग करता है लिखना चाहते हैं।

तो यहाँ क्या मैं (WebAdaptorUnitTest अंदर) अब तक मिल गया है:

@Test 
public void runShouldInvokeSubscriberInit() { 

    // Given 
    Subscriber mockSubscriber = mock(Subscriber.class); 
    WebAdaptor adaptor = new WebAdaptor(); 

    // When 
    adaptor.run(); 

    // Then 
    verify(mockSubscriber).init(); 
} 

जब मैं इस परीक्षण चलाने के लिए, वास्तविक Subscriber::init() विधि हो जाता है निष्पादित (मैं सांत्वना उत्पादन से बता सकते हैं और देख फ़ाइलें उत्पन्न किया जा रहा मेरे स्थानीय सिस्टम पर), mockSubscriber, जो कुछ भी नहीं (या वापसी) नहीं करना चाहिए।

मैं जाँच की है और फिर से जांच की गई: init, public है न static या final है, और यह void देता है। दस्तावेज़ों के मुताबिक, मॉकिटो को इस ऑब्जेक्ट को मजाक करने में कोई समस्या नहीं होनी चाहिए।

तो मुझे यह सोचने लगा: क्या मुझे adaptor के साथ mockSubscriber को स्पष्ट रूप से संबद्ध करने की आवश्यकता है? यह एक मामला है, तो आमतौर पर, निम्नलिखित सामान्य रूप से इसे ठीक होगा:

adaptor.setSubscriber(mockSubscriber); 

लेकिन बाद से मैं किसी भी तरह के सेटर नहीं जोड़ सकते हैं (कृपया मेरी टिप्पणी से ऊपर पढ़ें), मुझे लगता है मैं कैसे कर सकता है के रूप में एक नुकसान में हूँ इस तरह के एक संघ को मजबूर करें। तो, कई बहुत करीबी से संबंधित प्रश्न:

  • क्या कोई यह पुष्टि कर सकता है कि मैंने परीक्षण को सही तरीके से सेट किया है (मॉकिटो एपीआई का उपयोग करके)?
  • क्या लापता सेटटर के बारे में मेरा संदेह सही है? (क्या मुझे इन ऑब्जेक्ट्स को एक सेटर के माध्यम से जोड़ने की ज़रूरत है?)
  • यदि मेरा उपरोक्त संदेह सत्य है, और मैं WebAdaptor को संशोधित नहीं कर सकता, तो क्या मेरे निपटान में कोई circumventions हैं?

अग्रिम धन्यवाद!

+0

यह सीधे आपके प्रश्न का उत्तर नहीं है, लेकिन JMockit बहुत आसान मजाक ब्लैक बॉक्स इस तरह का बना देता है। क्या JMock यह आपके लिए एक विकल्प है? –

+0

सब्सक्राइबर इस वर्ग में कैसे तत्काल है? क्या आप जिस इंस्टेंस को नियंत्रित करते हैं उसे वापस करने के लिए तत्काल कोड को ओवरराइड करना संभव है? –

+0

रन() एकमात्र तरीका है जो सब्सक्राइबर का उपयोग करता है, इसलिए हर तरह से यह उस विधि के अंदर एक स्थानीय चर होना चाहिए। दोबारा, मैं कोड नहीं बदल सकता ... – IAmYourFaja

उत्तर

10

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

public class WebAdaptor { 

    public WebAdaptor(Subscriber subscriber) { /* Added a new constructor */ 
     this.subscriber = subscriber; 
    } 

    private Subscriber subscriber; 

    public void run() { 
     subscriber.init(); 
    } 
} 

अब आप वास्तविक वस्तु के बजाय नकली पर अपनी बातचीत को सत्यापित कर सकते हैं।

@Test 
public void runShouldInvokeSubscriberInit() { 

    // Given 
    Subscriber mockSubscriber = mock(Subscriber.class); 
    WebAdaptor adaptor = new WebAdaptor(mockSubscriber); // Use the new constructor 

    // When 
    adaptor.run(); 

    // Then 
    verify(mockSubscriber).init(); 
} 

तो निर्माता को सब्सक्राइबर जोड़ने सही दृष्टिकोण नहीं है, आप भी WebAdaptor एक कारखाने जो आप नियंत्रित से नए सब्सक्राइबर वस्तुओं का दृष्टांत को अनुमति देने के लिए एक कारखाने के उपयोग पर विचार कर सकता है। फिर आप कारखाने को प्रदाता नकली सब्सक्राइबरों पर नकल कर सकते हैं।

+0

WebAdaptor के लिए आपका स्निपेट अभी भी एक नया सब्सक्राइबर बनाने वाली रन() विधि है। FYI करें। –

+0

डेविड वी - मुझे ज़ोर से सुनने के बाद (जैसा कि मैंने यह प्रश्न लिखा था), आपके उत्तर और उपरोक्त टिप्पणियों के साथ संयुक्त, ऐसा लगता है कि 'वेबएडप्टर' कोड बदलना मेरा एकमात्र विकल्प है। आपके प्रतिक्रिया के लिए धन्येवाद! – IAmYourFaja

+2

अपने वेबएडाप्टर क्लास को मौजूदा कोड के साथ काम करने के लिए, आप एक नो-एर्ग कन्स्ट्रक्टर भी लेना चाहेंगे जो आपके नए कन्स्ट्रक्टर को कॉल करे। फिर कक्षा के मौजूदा गैर-परीक्षण उपयोग नो-एर्ग कन्स्ट्रक्टर का उपयोग कर सकते हैं। तो नया कन्स्ट्रक्टर 'सार्वजनिक वेबएडाप्टर() {यह होगा (नया सब्सक्राइबर());}'। साथ ही, सब्सक्राइबर तर्क वाला निर्माता पैकेज-निजी होना चाहिए। –

5

यदि आप उत्पादन कोड को बदलना नहीं चाहते हैं और फिर भी सब्सक्राइबर क्लास की कार्यक्षमता का मज़ाक उड़ा सकते हैं तो आपको PowerMock पर एक नज़र रखना चाहिए। यह मॉकिटो के साथ मिलकर काम करता है और आपको नई वस्तुओं के निर्माण की नकल करने की अनुमति देता है।

Subscriber mockSubscriber = mock(Subscriber.class); 
whenNew(Subscriber.class).withNoArguments().thenReturn(mockSubscriber); 

documentation for the PowerMock ढांचे में और विवरण समझाए गए हैं।

+0

पढ़ें [उदाहरण # 3 - नई वस्तुओं का मॉक निर्माण] (http://blog.jayway.com/2009/10/28/untestable-code-with-mockito-and-powermock/) (नीचे स्क्रॉल करें) और अधिक के लिए संपूर्ण उदाहरण – matsev

2

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

@Mock 
Subscriber mockSubscriber; 
WebAdaptor cut = new WebAdaptor(); 

@Before 
public void setup(){ 
    //sets the internal state of the field in the class under test even if it is private 
    MockitoAnnotations.initMocks(this); 

    //Now the whitebox functionality injects the dependent object - mockSubscriber 
    //into the object which depends on it - cut 
    Whitebox.setInternalState(cut, "subscriber", mockSubscriber); 
} 

@Test 
public void runShouldInvokeSubscriberInit() { 
    cut.run(); 
    verify(mockSubscriber).init(); 
} 

आशा इस मदद करता है :-)

+0

@zharvey यह सहायक था? या मैं गलत था? – Bala

+2

यह काम नहीं करेगा। जब cut.run() को नया, गैर-मजाक कहा जाता है, तो सब्सक्राइबर उदाहरण वेबएडाप्टर के अंदर बनाया जाएगा और मॉक किए गए संस्करण को प्रतिस्थापित करेगा। नए उदाहरण पर इनिट विधि को तब बुलाया जाएगा। –

+0

हां, आप सही हैं। मैनें इसे खो दिया। – Bala

1

आप सब्सक्राइबर अपने वर्तमान कार्यान्वयन में Mockito का उपयोग कर नकली नहीं कर सकता है।

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

public void run() { 

    subscriber = new Subscriber(); 
    // Mockito would need to jump in here 
    subscriber.init(); 
} 

डेविड वी का उत्तर सब्सक्राइबर को कन्स्ट्रक्टर में जोड़कर हल करता है। छुपा सब्सक्राइबर निर्माण को बनाए रखने वाला एक विकल्प सब्सक्राइबर को वेबएडाप्टर नो-एर्ग कन्स्ट्रक्टर में चालू करना होगा और फिर रन विधि को कॉल करने से पहले उस इंस्टेंस को प्रतिस्थापित करने के लिए प्रतिबिंब का उपयोग करना होगा।

आपका WebAdapter इस प्रकार दिखाई देगा,

public class WebAdaptor { 

    private Subscriber subscriber; 

    public WebAdaptor() { 
     subscriber = new Subscriber(); 
    } 

    public void run() {    
     subscriber.init(); 
    } 
} 

और आपको लगता है कि निजी क्षेत्र में निर्भरता सुई Springframework के परीक्षण मॉड्यूल से ReflectionTestUtils इस्तेमाल कर सकते हैं।

@Test 
public void runShouldInvokeSubscriberInit() { 

    // Given 
    Subscriber mockSubscriber = mock(Subscriber.class); 
    WebAdaptor adaptor = new WebAdaptor(); 
    ReflectionTestUtils.setField(adaptor "subscriber", mockSubscriber); 

    // When 
    adaptor.run(); // This will call mockSubscriber.init() 

    // Then 
    verify(mockSubscriber).init(); 
} 

ReflectionTestUtils वास्तव में जावा के प्रतिबिंब स्प्रिंग निर्भरता के बिना, एक ही मैन्युअल रूप से प्राप्त किया जा सकता है (और ज्यादा वरबोस रूप में दर्शा) के बारे में सिर्फ एक आवरण है।

Mockito के WhiteBox (जैसा कि बाला पता चलता है) ReflectionTestUtils के स्थान पर यहाँ काम करेगा, यह Mockito के आंतरिक पैकेज के भीतर निहित है तो मैं इसे से संकोच, YMMV।

2

आप मूल कोड को बदले बिना निर्माता कॉल उपहास करने के लिए इस्तेमाल किया जा सकता था PowerMock है

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(WebAdaptor.class) 
public class WebAdaptorTest { 
    @Test 
    public void testRunCallsSubscriberInit() { 
     final Subscriber subscriber = mock(Subscriber.class); 
     whenNew(Subscriber.class).withNoArguments().thenReturn(subscriber); 
     new WebAdaptor().run(); 
     verify(subscriber).init(); 
    } 
} 
संबंधित मुद्दे