2013-10-25 7 views
7

मैं इकाई परीक्षण करने के लिए नया हूँ तो मैं कुछ कमी हो सकती है, लेकिन मैं कैसे requirejs मॉड्यूल की संरचना करने के क्रम में उन्हें पूरी तरह से परीक्षण योग्य बनाने के लिए चाहिए लिखने के लिए? सुरुचिपूर्ण खुलासा मॉड्यूल पैटर्न पर विचार करें।कैसे परीक्षण योग्य requirejs मॉड्यूल

define([], function() { 
    "use strict"; 

    var func1 = function(){ 
     var data = func2(); 
    }; 
    var func2 = function(){ 
     return db.call(); 
    }; 

    return { 
     func1 : func1 
    } 
}); 

जहां तक ​​मैं इस के बारे में पता कर रहा हूँ requirejs मॉड्यूल के निर्माण के लिए सबसे आम पैटर्न है। अगर मैं गलत हूं कृपया मुझे सही! तो इस सरल परिदृश्य में मैं आसानी से वापसी मूल्यों और func1 के व्यवहार का परीक्षण कर सकता हूं क्योंकि यह वैश्विक है। हालांकि, ताकि func2 का परीक्षण करने में मैं भी यह संदर्भ है वापस जाने के लिए होगा। सही?

return { 
    func1 : func1, 
    _test_func2 : func2 
} 

यह कोड थोड़ा कम सुंदर बनाता है, लेकिन कुल मिलाकर अभी भी ठीक है। हालांकि, अगर मैं func2 नकली और Jasmine spy का उपयोग करके अपनी वापसी मान के स्थान पर करना चाहता था मैं के बाद से उस विधि को बंद करने के अंदर है में सक्षम नहीं होगा।

तो मेरे सवाल का requirejs मॉड्यूल की संरचना करने के लिए कैसे पूरी तरह से परीक्षण योग्य होने के लिए है? क्या मॉड्यूल पैटर्न को प्रकट करने से इस स्थिति के लिए बेहतर पैटर्न हैं?

उत्तर

5

यदि मॉड्यूल में फ़ंक्शन मॉड्यूल के अन्य फ़ंक्शंस को सीधे कॉल करते हैं (यानी मॉड्यूल के लिए स्थानीय संदर्भों का उपयोग करके), इन कॉल को बाहरी रूप से अवरुद्ध करने का कोई तरीका नहीं है। हालांकि, अगर आप अपने मॉड्यूल को बदलते हैं ताकि कार्यों अंदर यह मॉड्यूल के कार्यों उसी तरह कोड में फोन बाहर यह करता है, तो आप इन कॉल को रोक सकता।

define([], function() { 
    "use strict"; 

    var foo = function(){ 
     return exports.bar(); 
    }; 

    var bar = function(){ 
     return "original"; 
    }; 

    var exports = { 
     foo: foo, 
     bar: bar 
    }; 

    return exports; 
}); 

कुंजी है कि foobar का उपयोग करने के बजाय इसे सीधे कॉल करने के लिए exports माध्यम से चला जाता:

यहाँ एक उदाहरण है कि अनुमति होगी तुम क्या चाहते हो।

मैं एक runnable उदाहरण here ऊपर डाल दिया। spec/main.spec.js फ़ाइल है:

expect(moduleA.foo()).toEqual("original"); 

    spyOn(moduleA, "bar").andReturn("patched"); 

    expect(moduleA.foo()).toEqual("patched"); 

आप देखेंगे कि bar समारोह समझौता है, लेकिन foo पैचिंग से प्रभावित है।

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

var options = module.config(); 
var test = options && options.test; 

[...] 
// For testing only 
if (test) { 
    exports.__test = { 
     $modal: $modal, 
     reset: _reset, 
     is_terminating: _is_terminating 
    }; 
} 

requirejs विन्यास कॉन्फ़िगर करता है तो मेरी मॉड्यूल (config का प्रयोग करके) इतना है कि यह एक test विकल्प के लिए एक सही मूल्य पर सेट है तो निर्यात इसके अलावा एक __test प्रतीक शामिल होंगे: यहाँ वास्तविक कोड का एक उदाहरण मैं लिखा है है जिसमें कुछ अतिरिक्त आइटम शामिल हैं जिन्हें मैं निर्यात करना चाहता हूं जब मैं मॉड्यूल का परीक्षण कर रहा हूं। अन्यथा, ये प्रतीक उपलब्ध नहीं हैं।

संपादित करें: यदि क्या पहली विधि ऊपर exports के साथ आंतरिक कार्यों के लिए सभी कॉल्स उपसर्ग के लिए चल रहा है के बारे में आपको परेशान करती है, तो आप कुछ इस तरह कर सकता है:

define(["module"], function (module) { 
    "use strict"; 

    var debug = module.config().debug; 
    var exports = {}; 

    /** 
    * @function 
    * @param {String} name Name of the function to export 
    * @param {Function} f Function to export. 
    * @returns {Function} A wrapper for <code>f</code>, or <code>f</code>. 
    */ 
    var _dynamic = (debug ? 
     function (name, f) { 
      exports[name] = f; 
      return function() { 
       // This call allows for future changes to arguments passed.. 
       return exports[name].apply(this, arguments); 
      }; 
     } : 
     _dynamic = function (name, f) { return f; }); 

    var foo = function() { 
     return bar(1, 2, 3); 
    }; 

    var bar = _dynamic("bar", function (a, b, c) { 
     return "original: called with " + a + " " + b + " " + c; 
    }); 

    exports.foo = foo; 

    return exports; 
}); 

RequireJS विन्यास मॉड्यूल कॉन्फ़िगर करता है जब ऊपर debug सत्य है, यह _dynamicऔर द्वारा लिपटे कार्यों को निर्यात करता है जो स्थानीय प्रतीकों को प्रदान करता है जो उन्हें exports के बिना बिना संदर्भित करने की अनुमति देता है। यदि debug गलत है, तो फ़ंक्शन निर्यात नहीं किया गया है और लपेटा नहीं गया है। इस विधि को दिखाने के लिए मैंने example अपडेट किया है। उदाहरण में यह moduleB है।

+0

अरे, यह काम करने योग्य है, लेकिन कुछ हद तक परेशान है क्योंकि आपको मानचित्र के नाम से प्रत्येक विधि कॉल को उपसर्ग करना है। मैं कुछ दिनों के लिए अन्य सुझावों की प्रतीक्षा करूंगा :) – GEMI

+0

@GEMI मैंने अपना उत्तर किसी अन्य विधि से अपडेट किया है।इसके अलावा, केवल एक चीज जो दिमाग में आती है: हैं: अपनी जावास्क्रिप्ट को उपकरण जोड़ने के लिए प्रीप्रोसेस करें, या बंद करने के माध्यम से पंच करने के लिए अयोग्य वीएम हुक का उपयोग करें। – Louis

+0

अगर मैं इसे पहले पढ़ता हूं। मेरे सिर को कुचलने के लिए साइनऑन के साथ एक समारोह को रोकने के लिए बंद करने के माध्यम से कैसे प्राप्त किया गया था। महोदय, आपने मुझे बहुत समय बचाया! – dvcrn

7

क्या आप वाकई निजी फ़ंक्शन func2 का परीक्षण करना चाहते हैं?

मुझे लगता है कि जब वे निजी कार्यों के लिए परीक्षण लिखने का प्रयास करते हैं तो डेवलपर्स यूनिट परीक्षणों का बिंदु खो रहे हैं।

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

मेरी सलाह:

  1. डिजाइन अपने मॉड्यूल के लिए सार्वजनिक इंटरफ़ेस।
  2. कुछ अपेक्षित व्यवहार निर्दिष्ट करने के लिए सार्वजनिक इंटरफ़ेस के खिलाफ एक परीक्षण लिखें।
  3. उस परीक्षा को पारित करने के लिए आवश्यक कोड लागू करें।
  4. रिएक्टर (यदि आवश्यक हो)
  5. चरण 2 से दोहराएं, जब तक कि सभी कार्यक्षमता परीक्षणों द्वारा परिभाषित नहीं की जाती है, और सभी परीक्षण पास होते हैं।

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

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

func2 में कोड बहुत जटिल आप यह स्पष्ट रूप से परीक्षण करना चाहते हैं हो जाता है, तो यह एक अलग मॉड्यूल, आप सार्वजनिक इंटरफ़ेस के खिलाफ इकाई परीक्षण के साथ व्यवहार जहां परिभाषित में निकालें। छोटे, अच्छी तरह से परीक्षण मॉड्यूल के लिए लक्ष्य है जो सार्वजनिक इंटरफेस को समझने में आसान है।

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

+0

हाय, उत्तर के लिए धन्यवाद। शायद मुझे अपने प्रश्न को दोबारा शुरू करना चाहिए, क्योंकि असली समस्या यह है कि मैं आंतरिक तरीकों का नकल करने में सक्षम नहीं हूं। – GEMI

+0

लेकिन अगर आंतरिक अपने मॉड्यूल में डालते हैं, तो आप उन्हें नकल कर सकते हैं। – GarethOwen

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