nodejs非导出方法测试


nodejs写模块的时候经常会有一些函数,我们是不需要导出的,但是为了保证程序的正常,还是需要对它进行测试的,比如下面的一个模块

var sum = function(nums) {
  return nums.reduce(function(a, b) {
    return a + b
  })
}

module.exports = {
  doit: function(nums) {
    return sum(nums)
  }
}

其中的doit是导出的一个方法,mocha的测试代码如下:

var doit = require('./t.js').doit
var should = require('should')
describe('test', function() {
  it('doit should return array sum', function() {
    should(doit([1, 2, 3])).be.exactly(6)
  })
})

如果我们想直接测试sum函数怎么办?有两种方法

test环境时,导出到一个特定的对象上

var sum = function(nums) {
  return nums.reduce(function(a, b) {
    return a + b
  })
}

module.exports = {
  doit: function(nums) {
    return sum(nums)
  }
}

if (process.env.NODE_ENV === 'test') {
  module.exports._private = {
    sum: sum
  }
}

测试的代码修改如下:

var doit = require('./t.js').doit
var should = require('should')
describe('test', function() {
  it('doit should return array sum', function() {
    should(doit([1, 2, 3])).be.exactly(6)
  })

  if (process.env.NODE_ENV === 'test') {
    var sum = require('./t.js')._private.sum
    it('sum should return array sum', function() {
      should(sum([1, 2, 3])).be.exactly(6)
    })
  }
})

运行测试的时候,指明node的环境变量就行了NODE_ENV=test mocha s.spec.js 运行结果如下图:

使用rewire模块

rewire可以帮助我们修改和使用模块内非导出的变量及方法,比如上面的测试用例可以修改为:

var should = require('should')
var rewire = require('rewire')
var myModule = rewire('./t.js')

describe('test', function() {
  it('doit should return array sum', function() {
    should(myModule.doit([1, 2, 3])).be.exactly(6)
  })

  it('sum should return array sum', function() {
    should(myModule.__get__('sum')([1, 2, 3])).be.exactly(6)
  })
})

可以看到我们使用rewire包装过的模块,它有__set____get__方法,使用get就可以取到对应的方法进行测试,当然也可以进行一些mock

比如:

var should = require('should')
var rewire = require('rewire')
var myModule = rewire('./t.js')

myModule.__set__('sum', function(arry) {
  return 0
})

describe('test', function() {
  it('doit should return 0 while sum had reset ', function() {
    should(myModule.doit([1, 2, 3])).be.exactly(0)
  })
})

总结及分析

第一种方法算是一个约定,第二种方法更直接更彻底,推荐使用rewire的方式

作为一个有追求的程序员,了解一下rewire的原理还是相当有必要的

本质上rewire是把传入的js源码加工了一下,附加了一些代码,可以看这里

https://github.com/jhnns/rewire/blob/master/lib/rewire.js#L46

    appendix = "\n";
    appendix += "module.exports.__set__ = " + __set__Src + "; ";
    appendix += "module.exports.__get__ = " + __get__Src + "; ";
    appendix += "module.exports.__with__ = " + __with_Src + "; ";

    // Check if the module uses the strict mode.
    // If so we must ensure that "use strict"; stays at the beginning of the module.
    src = fs.readFileSync(targetPath, "utf8");
    if (detectStrictMode(src) === true) {
        prelude = ' "use strict"; ' + prelude;
    }

    moduleEnv.inject(prelude, appendix);
    moduleEnv.load(targetModule);

比如__get__方法,通过eval实现的, https://github.com/jhnns/rewire/blob/master/lib/__get__.js#L15

另外发现module里面很多没有在文档上写出来啊,比如https://github.com/jhnns/rewire/blob/master/lib/moduleEnv.js#L7 这里的wrapper,http://nodejs.org/api/modules.html 文档上啥也没有,只有看node的源码https://github.com/joyent/node/blob/master/lib/module.js#L61 才知道是从C里搞出来的,先不看这个C的源码了,有机会可以分解一下这个module.js的源码


推荐文章