नोड.जेएस मॉड्यूल में आंतरिक (गैर-निर्यात) फ़ंक्शन का उपयोग और परीक्षण कैसे करें?


181

मैं नोडज में आंतरिक (यानी निर्यात नहीं) कार्यों का परीक्षण करने के बारे में जानने की कोशिश कर रहा हूं (अधिमानतः मोचा या चमेली के साथ)। और मुझे कोई पता नहीं है!

मान लीजिए कि मेरे पास एक मॉड्यूल है:

function exported(i) {
   return notExported(i) + 1;
}

function notExported(i) {
   return i*2;
}

exports.exported = exported;

और निम्नलिखित परीक्षण (मोचा):

var assert = require('assert'),
    test = require('../modules/core/test');

describe('test', function(){

  describe('#exported(i)', function(){
    it('should return (i*2)+1 for any given i', function(){
      assert.equal(3, test.exported(1));
      assert.equal(5, test.exported(2));
    });
  });
});

क्या notExportedवास्तव में निर्यात किए बिना फ़ंक्शन का परीक्षण करने का कोई तरीका है क्योंकि यह उजागर होने का मतलब नहीं है?


1
हो सकता है कि किसी विशिष्ट वातावरण में परीक्षण करने के लिए अभी-अभी कार्यों का खुलासा हो? मैं यहाँ मानक प्रक्रिया नहीं जानता।
loganfsmyth

जवाबों:


243

rewire मॉड्यूल निश्चित रूप से जवाब है।

यहां एक अनएक्सपेक्टेड फ़ंक्शन तक पहुंचने और मोचा का उपयोग करके परीक्षण करने के लिए मेरा कोड है।

application.js:

function logMongoError(){
  console.error('MongoDB Connection Error. Please make sure that MongoDB is running.');
}

test.js:

var rewire = require('rewire');
var chai = require('chai');
var should = chai.should();


var app = rewire('../application/application.js');


logError = app.__get__('logMongoError'); 

describe('Application module', function() {

  it('should output the correct error', function(done) {
      logError().should.equal('MongoDB Connection Error. Please make sure that MongoDB is running.');
      done();
  });
});

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

व्यवहारिक समाधान। यह आगे जाने के लिए और इसे अपने परीक्षण ढांचे में जासूसों के साथ एकीकृत करने के लिए व्यस्त है। जैस्मिन के साथ काम करते हुए, मैंने इस रणनीति की कोशिश की ।
फ्रेंको

2
महान समाधान। क्या बाबेल प्रकार के लोगों के लिए एक कार्यशील संस्करण है?
चार्ल्स मेरियम

2
जेस्ट और टीएस-जेस्ट (टाइपस्क्रिप्ट) के साथ rewire का उपयोग करके मुझे निम्न त्रुटि मिलती है Cannot find module '../../package' from 'node.js':। आप यह देखा है?
क्लू

2
पुरस्‍कार में जस्‍ट के साथ एक अनुकूलता का मुद्दा है। जेस्ट कवरेज रिपोर्ट में rewire से बुलाए गए कार्यों पर विचार नहीं करेगा। वह कुछ हद तक उद्देश्य को हरा देता है।
लुटेरा 0606

10

चाल NODE_ENVपर्यावरण चर को किसी चीज़ की तरह सेट करना है testऔर फिर सशर्त रूप से निर्यात करना है।

मान लें कि आपने विश्व स्तर पर स्थापित मोचा नहीं बनाया है, तो आप अपनी एप्लिकेशन निर्देशिका की जड़ में एक मेकफाइल रख सकते हैं जिसमें निम्नलिखित शामिल हैं:

REPORTER = dot

test:
    @NODE_ENV=test ./node_modules/.bin/mocha \
        --recursive --reporter $(REPORTER) --ui bbd

.PHONY: test

यह फ़ाइल मोचा चलाने से पहले NODE_ENV सेट करता है। फिर आप make testकमांड लाइन पर अपने मोचा परीक्षण चला सकते हैं ।

अब, आप सशर्त रूप से अपने फ़ंक्शन को निर्यात कर सकते हैं जो आमतौर पर केवल तब निर्यात नहीं किया जाता है जब आपका मोचा परीक्षण चल रहा हो:

function exported(i) {
   return notExported(i) + 1;
}

function notExported(i) {
   return i*2;
}

if (process.env.NODE_ENV === "test") {
   exports.notExported = notExported;
}
exports.exported = exported;

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


8
यह एक हैक की तरह लगता है, क्या वास्तव में NODE_ENV ब्लॉक करने के बिना आंतरिक (गैर-निर्यात) कार्यों का परीक्षण करने का कोई तरीका नहीं है?
रायन हिर्श

2
यह बहुत बुरा है। यह इस समस्या को हल करने का सबसे अच्छा तरीका नहीं हो सकता है।
npiv

7

संपादित करें:

किसी मॉड्यूल का उपयोग करने से vmअनपेक्षित व्यवहार हो सकता है (उदाहरण के लिए instanceofऑपरेटर अब उन ऑब्जेक्ट्स के साथ काम नहीं करता है जो ऐसे मॉड्यूल में बनाए जाते हैं क्योंकि वैश्विक प्रोटोटाइप सामान्य रूप से लोड किए गए मॉड्यूल में उपयोग किए गए से भिन्न होते हैं require)। मैं अब नीचे दी गई तकनीक का उपयोग नहीं करता हूं और इसके बजाय rewire मॉड्यूल का उपयोग करता हूं । यह अद्भुत रूप से काम करता है। यहाँ मेरा मूल उत्तर है:

सरोश के जवाब पर विस्तार ...

यह थोड़ा हैकी लगता है, लेकिन मैंने एक सरल "test_utils.js" मॉड्यूल लिखा है, जो आपको अपने आवेदन मॉड्यूल में सशर्त निर्यात किए बिना वह करने की अनुमति देता है जो आप चाहते हैं:

var Script = require('vm').Script,
    fs     = require('fs'),
    path   = require('path'),
    mod    = require('module');

exports.expose = function(filePath) {
  filePath = path.resolve(__dirname, filePath);
  var src = fs.readFileSync(filePath, 'utf8');
  var context = {
    parent: module.parent, paths: module.paths, 
    console: console, exports: {}};
  context.module = context;
  context.require = function (file){
    return mod.prototype.require.call(context, file);};
  (new Script(src)).runInNewContext(context);
  return context;};

कुछ और चीजें हैं जो एक नोड मॉड्यूल के गोबल moduleऑब्जेक्ट में शामिल हैं जिन्हें अंदर जाने की भी आवश्यकता हो सकती हैcontext ऊपर दिए ऑब्जेक्ट है, लेकिन यह न्यूनतम सेट है जिसे मुझे काम करने की आवश्यकता है।

यहाँ mocha BDD का उपयोग करके एक उदाहरण दिया गया है:

var util   = require('./test_utils.js'),
    assert = require('assert');

var appModule = util.expose('/path/to/module/modName.js');

describe('appModule', function(){
  it('should test notExposed', function(){
    assert.equal(6, appModule.notExported(3));
  });
});

2
क्या आप एक उदाहरण दे सकते हैं कि आप एक गैर-निर्यात फ़ंक्शन का उपयोग rewireकैसे करते हैं ?
Matthias

1
हे माथियास, मैंने आपको एक उदाहरण दिया है जो मेरे उत्तर में ठीक है। यदि आप इसे पसंद करते हैं, तो शायद मेरे कुछ सवालों का जवाब दें? :) लगभग मेरे सभी प्रश्न 0 पर बैठे हैं और स्टैकऑवरफ्लो मेरे प्रश्नों को फ्रीज करने के बारे में सोच रहा है। X_X
एंथनी

2

जैस्मीन के साथ काम करते हुए, मैंने एंथनी मेफील्ड द्वारा प्रस्तावित समाधान के साथ गहराई से जाने की कोशिश की , जो रीवायर पर आधारित थी ।

मैंने निम्नलिखित फ़ंक्शन को लागू किया ( सावधानी : अभी तक पूरी तरह से परीक्षण नहीं किया गया है, बस एक व्यस्त रणनीति के रूप में साझा किया गया है) :

function spyOnRewired() {
    const SPY_OBJECT = "rewired"; // choose preferred name for holder object
    var wiredModule = arguments[0];
    var mockField = arguments[1];

    wiredModule[SPY_OBJECT] = wiredModule[SPY_OBJECT] || {};
    if (wiredModule[SPY_OBJECT][mockField]) // if it was already spied on...
        // ...reset to the value reverted by jasmine
        wiredModule.__set__(mockField, wiredModule[SPY_OBJECT][mockField]);
    else
        wiredModule[SPY_OBJECT][mockField] = wiredModule.__get__(mockField);

    if (arguments.length == 2) { // top level function
        var returnedSpy = spyOn(wiredModule[SPY_OBJECT], mockField);
        wiredModule.__set__(mockField, wiredModule[SPY_OBJECT][mockField]);
        return returnedSpy;
    } else if (arguments.length == 3) { // method
        var wiredMethod = arguments[2];

        return spyOn(wiredModule[SPY_OBJECT][mockField], wiredMethod);
    }
}

इस तरह से एक समारोह के साथ आप गैर-निर्यात वस्तुओं और गैर-निर्यात शीर्ष स्तर के कार्यों के दोनों तरीकों की जासूसी कर सकते हैं, निम्नानुसार है:

var dbLoader = require("rewire")("../lib/db-loader");
// Example: rewired module dbLoader
// It has non-exported, top level object 'fs' and function 'message'

spyOnRewired(dbLoader, "fs", "readFileSync").and.returnValue(FULL_POST_TEXT); // method
spyOnRewired(dbLoader, "message"); // top level function

फिर आप इस तरह की उम्मीदें लगा सकते हैं:

expect(dbLoader.rewired.fs.readFileSync).toHaveBeenCalled();
expect(dbLoader.rewired.message).toHaveBeenCalledWith(POST_DESCRIPTION);

0

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


0

मैंने एक बहुत ही सरल तरीका खोजा है जो आपको उन आंतरिक कार्यों का परीक्षण करने, जासूसी करने और मॉक करने की अनुमति देता है :

मान लें कि हमारे पास एक नोड मॉड्यूल इस तरह है:

mymodule.js:
------------
"use strict";

function myInternalFn() {

}

function myExportableFn() {
    myInternalFn();   
}

exports.myExportableFn = myExportableFn;

अब हम परीक्षण करना चाहते हैं और जासूसी और नकली myInternalFn , जबकि यह उत्पादन में निर्यात नहीं हम इस तरह फ़ाइल में सुधार करने के लिए है:

my_modified_module.js:
----------------------
"use strict";

var testable;                          // <-- this is new

function myInternalFn() {

}

function myExportableFn() {
    testable.myInternalFn();           // <-- this has changed
}

exports.myExportableFn = myExportableFn;

                                       // the following part is new
if( typeof jasmine !== "undefined" ) {
    testable = exports;
} else {
    testable = {};
}

testable.myInternalFn = myInternalFn;

अब आप myInternalFnहर जगह परीक्षण, जासूसी और नकली का उपयोग कर सकते हैं जहां आप इसका उपयोग करते हैं testable.myInternalFnऔर उत्पादन में इसका निर्यात नहीं किया जाता है


0

यह अनुशंसित अभ्यास नहीं है, लेकिन यदि आप rewire@Antoine द्वारा सुझाए अनुसार उपयोग नहीं कर सकते हैं , तो आप हमेशा फ़ाइल को पढ़ सकते हैं और उपयोग कर सकते हैं eval()

var fs = require('fs');
const JsFileString = fs.readFileSync(fileAbsolutePath, 'utf-8');
eval(JsFileString);

मुझे यह उपयोगी लगा जबकि यूनिट एक क्लाइंट सिस्टम के लिए क्लाइंट-साइड JS फ़ाइलों का परीक्षण कर रही थी।

जेएस फाइलें windowबिना किसी बयान require(...)और module.exportsबयान के बहुत सारे वैश्विक चर स्थापित करेगी (वैसे भी इन बयानों को हटाने के लिए वेबपैक या ब्राउजर की तरह कोई मॉड्यूल बंडल उपलब्ध नहीं था)।

पूरे कोडबेस को रिफलेक्टर करने के बजाय, इसने हमें अपने क्लाइंट-साइड JS में इकाई परीक्षणों को एकीकृत करने की अनुमति दी।

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