使用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 ),其中包含代码示例。