在Node.js中模拟模块进行unit testing

我想unit testingnode.js模块中的一些function。 我认为嘲笑第三个模块会有帮助。 特别要避免碰到数据库

# models/account.coffee register = (email, password)-> sha_sum.update(password) pw = sha_sum.digest('hex') user = email: email password: sha_sum.digest('hex') users_db.save user, (err, doc)-> register_callback(err) account_module = register: register module.exports = account_module 

这是我想testing的模块

 # routes/auth.coffee account = require '../models/account' exports.auth = post_signup: (req, res)-> email = req.body.email password = req.body.password if email and password account.register(email, password) res.send 200 else res.send 400 

我想能够testing打在这个URL与正确的身体在后调用account.register函数,但我不希望testing打到数据库。 我可能还没有实现帐户模块。

jasmine spec#specs / auth.test.coffee描述了'注册', – >

  request = require 'request' it 'should signup a user with username and password', (done)-> spyOn(account, 'register') # this does not work, account.register still called url = root + '/signup' headers = "Content-Type": "application/json" data = email: 'user@email.com' password: 'pw' body = JSON.stringify(data) request {url: url, method: 'POST',json: data, headers: headers }, (err, response, body)-> expect(response.statusCode).toEqual(200) done() 

我已经研究了几个模拟库node.js( https://github.com/easternbloc/Syringe,https://github.com/felixge/node-sandboxed-module ),但迄今没有成功。 无论我在规范中做什么, account.register都会被执行。 这整个方法是否有缺陷?

我使用摩卡作为testing框架,用颂歌来嘲笑,存根和间谍。 我build议你把你的账户模块委托给auth.coffee模块,并像这样模拟它:

 exports.init = function (account) { // set account object } 

所以从摩卡testing中,您可以创build一个虚拟的帐户对象,并在实际testing中用sinon模拟它。

 describe('some tests', function () { var account, response, testObject; beforeEach(function () { account = { register: function () { } }; response = { send: function () { } }; testObject = require('./auth'); testObject.init(account); }); it('should test something', function () { var req = { body: { email: ..., password: .... } }, // the request to test resMock = sinon.mock(response), registerStub = sinon.stub(account, 'register'); // the request expectations resMock.expect('send').once().withArgs(200); // the stub for the register method to have some process registerStub.once().withArgs('someargs'); testObject.auth(req. response); resMock.verify(); }); }); 

对不起,不写在coffescript,但我不习惯。

斯蒂芬的解决scheme的作品。 我只是添加一些细节。

  describe 'register', -> account = response = routes_auth = null beforeEach -> account = register: (email, pw, callback)-> if email is 'valid@email.com' callback(null, 1) else err = 'error' callback(err, 0) response = send: -> {} routes_auth = require('../routes/auth').init(account) it 'should register a user with email and pw', (done)-> req = body: email: 'valid@email.com' password: 'pw' resMock = sinon.mock(response) resMock.expects('send').once().withArgs(200) routes_auth.post_register(req, response) resMock.verify() done() it 'should not register a user without email', ()-> req = body: password: 'pw' resMock = sinon.mock(response) resMock.expects('send').once().withArgs(400) routes_auth.post_register(req, response) resMock.verify() 

routes/auth.coffee模块…

 exports.init = (account)-> get_available: (req, res)-> email = req.param.email if not email? or email.length < 1 res.send 400 return account.available email, (err, doc)-> console.log 'get_available', err, doc if err then res.send 401 else res.send 200 post_register: (req, res)-> email = req.body.email password = req.body.password if email and password account.register email, password, (err, doc)-> if err then res.send 401 else res.send 200 else res.send 400 

我一直在轻轻地使用嘲讽和testing框架的mocha和mocha ,以及用于BDD风格testing的should.js。 下面是我的样本unit testing的样子:

 describe('#Store() ', function () { it('will delegate the store to the CacheItem and CacheKey', function () { var actualCacheKey, actualConnMgr, actualConfig, actualLogger, actualRequest; var actualKeyRequest, actualKeyConfig; gently.expect( CacheKey, 'CreateInstance', function (apiRequest, config) { actualKeyRequest = apiRequest; actualKeyConfig = config; return mockCacheKey; }); gently.expect( CacheItem, 'CreateInstance', function (cacheKey, connectionManager, config, logger, apiRequest) { actualCacheKey = cacheKey; actualConnMgr = connectionManager; actualConfig = config; actualLogger = logger; actualRequest = apiRequest; return mockCacheItem; }); var actualApiRequest, actualCallback; gently.expect(mockCacheItem, 'Store', function (request, callback) { actualApiRequest = request; actualCallback = callback; }); var callback = function () {}; var apiResponse = {'item': 'this is a sample response from SAS'}; Cache.GetInstance(connMgr, config, logger).Store(apiRequest, apiResponse, callback); mockCacheKey.should.be.equal(actualCacheKey, 'The cachkeKey to CacheItem.CreateIntsance() did not match'); connMgr.should.be.equal( actualConnMgr, 'The connection manager to CacheItem.CreateInstance() did not match'); config.should.be.equal(actualConfig, 'The config to CacheItem.CreateInstance() did not match'); logger.should.be.equal(actualLogger, 'The logger to CacheItem.Createinstance did not match'); apiRequest.should.be.equal(actualRequest, 'The request to CacheItem.Createinstance() did not match'); apiRequest.should.be.equal(actualKeyRequest, 'The request to CacheKey.CreateInstance() did not match'); config.should.be.equal(actualKeyConfig, 'The config to CacheKey.CreateInstance() did not match'); callback.should.be.equal(actualCallback, 'The callback passed to CacheItem.Store() did not match'); apiResponse.should.be.equal(actualApiRequest, 'The apiRequest passed to CacheItem.Store() did not match'); }); });