मोचा और नोड.जेएस के साथ निजी कार्यों का यूनिट परीक्षण


131

मैं नोड.जेएस के लिए लिखे गए एप्लिकेशन को यूनिट टेस्ट करने के लिए मोचा का उपयोग कर रहा हूं

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

उदाहरण:

मेरे पास इस तरह के बहुत सारे कार्य हैं foobar.js

function private_foobar1(){
    ...
}

function private_foobar2(){
    ...
}

और सार्वजनिक रूप से निर्यात किए गए कुछ कार्य:

exports.public_foobar3 = function(){
    ...
}

परीक्षण का मामला इस प्रकार संरचित है:

describe("private_foobar1", function() {
    it("should do stuff", function(done) {
        var stuff = foobar.private_foobar1(filter);
        should(stuff).be.ok;
        should(stuff).....

जाहिर है कि यह काम नहीं करता है, क्योंकि private_foobar1निर्यात नहीं किया जाता है।

यूनिट-टेस्ट प्राइवेट तरीकों का सही तरीका क्या है? क्या मोचा के पास ऐसा करने के लिए कुछ अंतर्निहित विधियां हैं?


जवाबों:


64

यदि फ़ंक्शन मॉड्यूल द्वारा निर्यात नहीं किया जाता है, तो इसे मॉड्यूल के बाहर परीक्षण कोड द्वारा नहीं बुलाया जा सकता है। यही कारण है कि जावास्क्रिप्ट कैसे काम करता है, और मोचा स्वयं इसे दरकिनार नहीं कर सकता है।

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

"पर्यावरण" शब्द का यहाँ बहुत उपयोग किया जाता है। इसका मतलब जाँच process.envया कुछ और हो सकता है जो मॉड्यूल से संवाद कर सकता है "अब आपका परीक्षण किया जा रहा है"। जिन उदाहरणों के लिए मुझे ऐसा करना पड़ा है वे एक आवश्यकता वातावरण में थे, और मैंने module.configइस उद्देश्य के लिए उपयोग किया है ।


2
सशर्त रूप से निर्यात मान ES6 मॉड्यूल के साथ संगत प्रतीत नहीं होता है। मैं हो रहा हूँSyntaxError: 'import' and 'export' may only appear at the top level
aij

1
@aij हाँ ES6 स्थिर निर्यात की वजह से आप उपयोग नहीं कर सकते import, exportएक ब्लॉक के अंदर। आखिरकार आप सिस्टम लोडर के साथ ES6 में इस तरह की चीज को पूरा कर पाएंगे। अब इसके चारों ओर पाने का एक तरीका module.exports = process.env.NODE_ENV === 'production' ? require('prod.js') : require('dev.js')उन संबंधित फाइलों में अपने es6 कोड अंतरों का उपयोग करना और संग्रहीत करना है।
cchamberlain

2
मुझे लगता है कि यदि आपके पास पूर्ण कवरेज है, तो आप अपने सभी निजी कार्यों का परीक्षण कर रहे हैं, चाहे आपने उन्हें उजागर किया हो या नहीं।
जिग्गी

1
@ आप सशर्त रूप से निर्यात कर सकते हैं ... इस उत्तर को देखें: stackoverflow.com/questions/39583958/…
RayLoveless

187

रीवायर मॉड्यूल की जाँच करें । यह आपको एक मॉड्यूल के भीतर निजी चर और कार्यों को प्राप्त करने (और हेरफेर) करने की अनुमति देता है।

तो आपके मामले में उपयोग कुछ इस तरह होगा:

var rewire = require('rewire'),
    foobar = rewire('./foobar'); // Bring your module in with rewire

describe("private_foobar1", function() {

    // Use the special '__get__' accessor to get your private function.
    var private_foobar1 = foobar.__get__('private_foobar1');

    it("should do stuff", function(done) {
        var stuff = private_foobar1(filter);
        should(stuff).be.ok;
        should(stuff).....

3
@Jaro मेरा अधिकांश कोड या तो AMD मॉड्यूल के रूप में है, जो rewire को संभालने में असमर्थ है (क्योंकि AMD मॉड्यूल फ़ंक्शन हैं लेकिन rewire "फ़ंक्शन के भीतर चर" को संभाल नहीं सकता है)। या ट्रांसप्लड किया जाता है, एक और परिदृश्य जो rewire नहीं संभाल सकता है। वास्तव में, जो लोग rewire को देखने जा रहे हैं, वे इसका उपयोग करने का प्रयास करने से पहले सीमाओं (पहले जुड़े हुए) को अच्छी तरह से पढ़ लेंगे। मेरे पास एक भी ऐसा ऐप नहीं है जो a) "निजी" सामान के निर्यात की आवश्यकता है और b) rewire की सीमा में नहीं चलता है।
लुई

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

रेस्ट जेस्ट के ऑटो-मॉकिंग टूल के साथ अच्छी तरह से नहीं खेलता है। मैं अभी भी जेस्ट के लाभों का लाभ उठाने और कुछ निजी संस्करणों तक पहुंचने का एक रास्ता ढूंढ रहा हूं।
btburton42

इसलिए मैंने यह काम करने की कोशिश की, लेकिन मैं टाइपस्क्रिप्ट का उपयोग कर रहा हूं, जिसका मैं अनुमान लगा रहा हूं कि यह समस्या पैदा कर रहा है। मूल रूप से मुझे निम्नलिखित त्रुटि मिलती है Cannot find module '../../package' from 'node.js':। इससे कोई परिचित है?
क्लू

rewire में ठीक काम कर रहा है .ts, typescriptमैं ts-node @clu
मुथुकुमार

24

यहां अपने ब्लॉग पर Google इंजीनियर फिलिप वाल्टन द्वारा बताए गए अपने निजी तरीकों का परीक्षण करने के लिए वास्तव में अच्छा वर्कफ़्लो है

सिद्धांत

  • अपना कोड सामान्य रूप से लिखें
  • ऑब्जेक्ट के लिए अपने निजी तरीकों को एक अलग कोड ब्लॉक में बांधें, _उदाहरण के लिए इसे चिह्नित करें
  • उस कोड को चारों ओर से शुरू और समाप्ति टिप्पणियों के आधार पर घेरें

फिर उत्पादन बिल्ड के लिए इस ब्लॉक को स्ट्रिप करने के लिए एक बिल्ड टास्क या अपने स्वयं के बिल्ड सिस्टम (उदाहरण के लिए ग्रंट-स्ट्रिप-कोड) का उपयोग करें।

आपके परीक्षणों का निर्माण आपके निजी एपीआई तक पहुंच है, और आपके उत्पादन में कोई अंतर नहीं है।

टुकड़ा

अपना कोड इस प्रकार लिखें:

var myModule = (function() {

  function foo() {
    // private function `foo` inside closure
    return "foo"
  }

  var api = {
    bar: function() {
      // public function `bar` returned from closure
      return "bar"
    }
  }

  /* test-code */
  api._foo = foo
  /* end-test-code */

  return api
}())

और आपके ग्रंट कार्य जैसे हैं

grunt.registerTask("test", [
  "concat",
  "jshint",
  "jasmine"
])
grunt.registerTask("deploy", [
  "concat",
  "strip-code",
  "jshint",
  "uglify"
])

अधिक गहरा

बाद के एक लेख में , यह "निजी विधियों के परीक्षण" के "क्यों" की व्याख्या करता है


1
एक वेबकिट प्लगइन भी मिला जो दिखता है कि यह एक समान वर्कफ़्लो का समर्थन कर सकता है: वेबपैक-स्ट्रिप-ब्लॉक
JRulle

21

यदि आप इसे सरल रखना पसंद करते हैं, तो बस निजी सदस्यों को निर्यात करें, लेकिन स्पष्ट रूप से सार्वजनिक एपीआई से कुछ सम्मेलन के साथ अलग हो जाते हैं, उदाहरण के लिए उन्हें _एक निजी वस्तु के तहत एक घोंसले के साथ उपसर्ग करें ।

var privateWorker = function() {
    return 1
}

var doSomething = function() {
    return privateWorker()
}

module.exports = {
    doSomething: doSomething,
    _privateWorker: privateWorker
}

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

2
आप नेस्टेड सिंटैक्स {... निजी : {कार्यकर्ता: कार्यकर्ता}}
जेसन

2
यदि मॉड्यूल सभी शुद्ध कार्य करता है, तो मुझे ऐसा करने के लिए कोई नकारात्मक पहलू नहीं दिखता है। यदि आप राज्य रख रहे हैं और परिवर्तन कर रहे हैं, तो सावधान रहें ...
Ziggy

5

मैंने इस उद्देश्य के लिए एक एनपीएम पैकेज बनाया है जो आपको उपयोगी लग सकता है: आवश्यकता से

मूल रूप से आप गैर-सार्वजनिक तरीकों को उजागर करते हैं:

module.testExports = {
    private_foobar1: private_foobar1,
    private_foobar2: private_foobar2,
    ...
}

ध्यान दें: testExports कोई भी मान्य नाम हो सकता है, exportsजो पाठ्यक्रम को छोड़कर ।

और एक अन्य मॉड्यूल से:

var requireFrom = require('require-from');
var private_foobar1 = requireFrom('testExports', './path-to-module').private_foobar1;

1
मैं इस पद्धति का कोई व्यावहारिक लाभ नहीं देखता हूं। यह "निजी" प्रतीकों को अधिक निजी नहीं बनाता है। (कोई भी व्यक्ति requireFromसही मापदंडों के साथ कॉल कर सकता है ।) इसके अलावा, यदि मॉड्यूल textExportsलोड होने से पहलेrequire कॉल द्वारा लोड किया गया है, तो वापस आ जाएगा । (मैंने अभी इसका परीक्षण किया है।) जबकि मॉड्यूल के लोड ऑर्डर को नियंत्रित करना अक्सर संभव होता है, यह हमेशा व्यावहारिक नहीं होता है। (एसओ पर कुछ मोचा सवालों के सबूत के रूप में।) यह समाधान भी आम तौर पर एएमडी-प्रकार के मॉड्यूल के साथ काम नहीं करेगा। (मैं परीक्षण के लिए एक दैनिक आधार पर नोड में एएमडी मॉड्यूल लोड करता हूं।) requireFromrequireFromundefined
लुइस

यह AMD मॉड्यूल के साथ काम नहीं करना चाहिए! Node.js common.js का उपयोग करता है और यदि आप AMD का उपयोग करने के लिए इसे बदल रहे हैं, तो आप इसे आदर्श से बाहर कर रहे हैं।
jemiloii

@JemiloII सैकड़ों डेवलपर्स एएमडी मॉड्यूल का परीक्षण करने के लिए रोजाना Node.js का उपयोग करते हैं। ऐसा करने में "आदर्श से बाहर" कुछ भी नहीं है। सबसे आप कह सकते हैं कि नोड.जेएस एक एएमडी लोडर के साथ नहीं आता है, लेकिन यह बहुत कुछ नहीं कह रहा है, क्योंकि नोड जो अपने लोडर को विकसित करने के लिए डेवलपर्स के देखभाल के लिए लोड करने का विस्तार करने के लिए स्पष्ट हुक प्रदान करता है।
लुईस

यह आदर्श से बाहर है। यदि आपको मैन्युअल रूप से एक एएमडी लोडर शामिल करना है, तो यह नोड के लिए आदर्श नहीं है। मैं शायद ही कभी AMD को नोड.जेएस कोड के लिए देखता हूं। मैं इसे ब्राउज़र के लिए देखूंगा, लेकिन नोड। नहीं, मैं यह नहीं कह रहा हूं कि ऐसा नहीं किया जा रहा है, सिर्फ सवाल और यह जवाब जिस पर हम टिप्पणी कर रहे हैं, amd मॉड्यूल के बारे में कुछ नहीं कहेंगे। तो बिना किसी को बताए कि वे एक एम डी लोडर, नोड एक्सपोर्ट का उपयोग कर रहे हैं, एम डी के साथ काम नहीं करना चाहिए। हालाँकि, मैं ध्यान देना चाहता हूँ, हो सकता है कि एसस 6 निर्यात के साथ आम रास्ते पर हो। मुझे उम्मीद है कि एक दिन हम सभी सिर्फ एक निर्यात विधि का उपयोग कर सकते हैं।
19 फरवरी को jemiloii

4

मैंने एक अतिरिक्त फ़ंक्शन जोड़ा है जिसे मैं आंतरिक () नाम देता हूं और वहां से सभी निजी कार्यों को वापस करता हूं । यह आंतरिक () फ़ंक्शन तब निर्यात किया जाता है। उदाहरण:

function Internal () {
  return { Private_Function1, Private_Function2, Private_Function2}
}

// Exports --------------------------
module.exports = { PublicFunction1, PublicFunction2, Internal }

आप इस तरह के आंतरिक कार्यों को कॉल कर सकते हैं:

let test = require('.....')
test.Internal().Private_Function1()

मुझे यह घोल सबसे अच्छा लगता है क्योंकि:

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

2

मैंने @barwin उत्तर का अनुसरण किया और जाँच की कि कैसे इकाई परीक्षण rewire मॉड्यूल के साथ किए जा सकते हैं । मैं पुष्टि कर सकता हूं कि यह समाधान बस काम करता है।

मॉड्यूल को दो भागों में आवश्यक होना चाहिए - सार्वजनिक एक और निजी एक। सार्वजनिक कार्यों के लिए आप मानक तरीके से कर सकते हैं:

const { public_foobar3 } = require('./foobar');

निजी दायरे के लिए:

const privateFoobar = require('rewire')('./foobar');
const private_foobar1 = privateFoobar .__get__('private_foobar1');
const private_foobar2 = privateFoobar .__get__('private_foobar2');

विषय के बारे में अधिक जानने के लिए, मैंने पूर्ण मॉड्यूल परीक्षण के साथ एक कार्यशील उदाहरण बनाया, परीक्षण में निजी और सार्वजनिक क्षेत्र शामिल हैं।

अधिक जानकारी के लिए मैं आपको इस लेख ( https://medium.com/@macsikora/how-to-test-pStreet-functions-of-es6-module-fb8c1345b25f ) को पूरी तरह से विषय की जाँच करने के लिए प्रोत्साहित करता हूं , इसमें कोड नमूने शामिल हैं।


2

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

उदाहरण के लिए, सार्वजनिक रूप से एक ही फ़ाइल में निजी तरीके रखने के बजाय इस तरह ...

src / बात / PublicInterface.js


function helper1 (x) {
    return 2 * x;
}

function helper2 (x) {
    return 3 * x;
}

export function publicMethod1(x) {
    return helper1(x);
}

export function publicMethod2(x) {
    return helper1(x) + helper2(x);
}

... आप इसे इस तरह से विभाजित करते हैं:

src / बात / PublicInterface.js

import {helper1} from './internal/helper1.js';
import {helper2} from './internal/helper2.js';

export function publicMethod1(x) {
    return helper1(x);
}

export function publicMethod2(x) {
    return helper1(x) + helper2(x);
}

src / बात / आंतरिक / helper1.js

export function helper1 (x) {
    return 2 * x;
}

src / बात / आंतरिक / helper2.js

export function helper2 (x) {
    return 3 * x;
}

इस तरह, आप आसानी से परीक्षण कर सकते हैं helper1और helper2जैसा कि, रेवर और अन्य "जादू" का उपयोग किए बिना (जो, मैंने पाया है, डिबगिंग करते समय अपने स्वयं के दर्द बिंदु हैं, या जब आप टाइपस्क्रिप्ट की ओर अपना कदम बनाने की कोशिश करते हैं, तो गरीब का उल्लेख करने के लिए नहीं। नए सहयोगियों के लिए समझ)। और उन्हें उप-फ़ोल्डर में बुलाया जा रहा है internal, या ऐसा कुछ, अनजाने स्थानों में उनके आकस्मिक उपयोग से बचने में मदद करेगा।


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


1

परीक्षण के लिए निजी तरीके उपलब्ध करने के लिए, मैं यह करता हूं:

const _myPrivateMethod: () => {};

const methods = {
    myPublicMethod1: () => {},
    myPublicMethod2: () => {},
}

if (process.env.NODE_ENV === 'test') {
    methods._myPrivateMethod = _myPrivateMethod;
}

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