使用mocha和node.js对私有函数进行unit testing

我正在使用摩卡来testing为node.js编写的应用程序

我想知道是否可以unit testing没有在模块中导出的函数。

例:

我在foobar.js定义了很多这样的函数

 function private_foobar1(){ ... } function private_foobar2(){ ... } 

和一些作为公众出口的function:

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

testing用例的结构如下:

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

显然这是行不通的,因为private_foobar1没有被导出。

unit testing私有方法的正确方法是什么? 摩卡是否有一些内置的方法来做到这一点?

如果该function没有被模块输出,则不能被模块外的testing代码调用。 这是由于JavaScript是如何工作的,摩卡本身无法绕过这一点。

在less数情况下,我确定testing一个私有函数是正确的事情,我所做的是设置一些环境variables,我的模块检查,以确定它是否在testing设置运行。 如果它在testing设置中运行,那么它将导出额外的函数,然后我可以在testing期间调用它。

在这里松散地使用“环境”一词。 这可能意味着检查process.env或其他可以传递给模块“你现在正在testing”的东西。 我不得不这样做的情况是在RequireJS环境中,并且为此使用了module.config

退房rewire模块。 它允许你获取(和操作)模块中的私有variables和函数。

所以在你的情况下,使用会是这样的:

 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)..... 

这是一个很好的工作stream程,可以testing您的博客Philip工程师Philip Walton所解释的私有方法 。

原理

  • 正常写你的代码
  • 将您的私有方法绑定到单独的代码块中的对象,例如用_标记
  • 通过开始和结束评论围绕代码块

然后使用构build任务或您自己的构build系统(例如grunt-strip-code)来剥离生产版本的此块。

您的testing版本可以访问您的私有API,而您的生产版本则没有。

片段

写下你的代码:

 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 }()) 

而你这样的咕tasks任务

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

更深入

在后面的文章中 ,它解释了“testing私人方法”的“为什么”

国际海事组织不需要增加所有这些私人/公共的解决办法,更不用说额外的工具了。

你也可以只导出私有成员,但是可以用公约来清楚地分开公共API,例如用一个_ (普通的)前缀这些成员,或者把它们嵌套在一个私有对象下面。

 var privateWorker = function() { return 1 } var doSomething = function() { return privateWorker() } module.exports = { doSomething: doSomething, _privateWorker: privateWorker } 

它可能来自Python,但我已经在JavaScript中看到了足够的东西。

我为此制作了一个npm包,您可能会发现它很有用: require-from

基本上你通过以下途径公开非公开的方法:

 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; 

我跟着@barwin的答案,并检查如何使用rewire模块进行unit testing。 我可以确认这个解决scheme简单的工作。

模块应该分为两部分 – 公共的和私人的。 对于公共职能你可以用标准的方式来做到这一点:

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

对于私人范围:

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

为了更多地了解这个主题,我创build了一个完整的模块testing的工作示例,testing包括私有和公共范围。

有关详细信息,我鼓励您查看完整描述该主题的文章( https://medium.com/@macsikora/how-to-test-private-functions-of-es6-module-fb8c1345b25f ),其中包含代码示例。