如何访问和测试node.js模块中的内部(非导出)功能?

xav*_*ard 142 unit-testing mocha.js node.js jasmine

我试图弄清楚如何在nodejs中测试内部(即未导出)函数(最好使用mocha或jasmine).我不知道!

假设我有一个这样的模块:

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

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

exports.exported = exported;
Run Code Online (Sandbox Code Playgroud)

以下测试(摩卡):

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));
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

有没有办法对notExported函数进行单元测试而不实际导出它,因为它不是要暴露的?

Ant*_*ony 203

联控模块是绝对的答案.

这是我访问未导出函数并使用Mocha测试它的代码.

application.js中:

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

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();
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 使用带有 jest 和 ts-jest (typescript) 的 rewire 我收到以下错误:`Cannot find module '../../package' from 'node.js'`。你见过这个吗? (3认同)
  • Rewire 与 jest 存在兼容性问题。Jest 不会考虑覆盖率报告中从 rewire 调用的函数。这有点违背了目的。 (3认同)
  • 这绝对应该是最好的答案.它不需要使用NODE_ENV特定的导出重写所有现有模块,也不需要将模块作为文本读取. (2认同)
  • 很好的解决方案。有针对Babel型人士的工作版本吗? (2认同)
  • 是的,这是*一个解决方案*。唯一的问题是 Jest 的测试覆盖率报告中没有考虑重新接线的模块。 (2认同)

Mat*_*ley 10

诀窍是将NODE_ENV环境变量设置为类似test然后有条件地导出它.

假设您没有全局安装mocha,您可以在app目录的根目录中包含Makefile,其中包含以下内容:

REPORTER = dot

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

.PHONY: test
Run Code Online (Sandbox Code Playgroud)

此make文件在运行mocha之前设置NODE_ENV.然后,您可以make test在命令行中运行mocha测试.

现在,您可以有条件地导出通常只有在运行mocha测试时才导出的函数:

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;
Run Code Online (Sandbox Code Playgroud)

另一个答案建议使用vm模块来评估文件,但这不起作用并抛出错误,指出未定义导出.

  • 这看起来像是一个黑客,如果NODE_ENV阻止,是否真的没有办法测试内部(非导出)函数而不执行该操作? (7认同)
  • 那太讨厌了.这不是解决此问题的最佳方法. (2认同)

mhe*_*ess 7

编辑:

使用模块加载vm可能会导致意外行为(例如,instanceof操作员不再使用在此类模块中创建的对象,因为全局原型与通常加载的模块中使用的原型不同require).我不再使用以下技术,而是使用重新布线模块.它运作得很好.这是我原来的答案:

阐述srosh的回答......

感觉有点hacky,但是我写了一个简单的"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;};
Run Code Online (Sandbox Code Playgroud)

节点模块的gobal 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));
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 你能举例说明如何使用`rewire`访问非导出函数吗? (2认同)

小智 5

我一直在使用不同的方法,没有任何依赖项:使用 __testing 导出我想要测试的所有本地函数,该值取决于 NODE_ENV,因此只能在测试中访问:

// file.ts
const localFunction = () => console.log('do something');
const localFunciton2 = () => console.log('do something else');
export const exportedFunction = () => {
    localFunction();
    localFunciton2();
}
export const __testing = (process.env.NODE_ENV === 'test') ? {
    localFunction, localFunction2
} : void 0;

// file.test.ts
import { __testing, exportedFunction } from './file,ts'
const { localFunction, localFunction2 } = __testing!;
// Now you can test local functions
Run Code Online (Sandbox Code Playgroud)