2013-04-03 48 views
15

我想單元測試node.js模塊中的一些函數。我認爲嘲笑第三個模塊會有所幫助。特別要避免撞上數據庫在Node.js中模擬單元測試模塊

# 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 

這是我想測試

# 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 

我希望能夠測試擊中在後這個網址與正確的身體調用的模塊account.register函數,但我不想測試打到數據庫。我可能還沒有實施帳戶模塊。

茉莉花規範 #規格/ 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: '[email protected]' 
      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/Syringehttps://github.com/felixge/node-sandboxed-module),但到目前爲止,沒有成功。無論我在規範中嘗試什麼,account.register總會得到執行。這整個方法是否有缺陷?

+0

它看起來像horaa(https://github.com/arunoda/horaa)可能工作 –

+0

嘗試sinon.js,一個優秀的存根/間諜/模擬/ mockServer庫。 – nottinhill

回答

15

我使用mocha作爲測試框架,sinon用於模擬,存根和間諜。我建議你委託帳戶模塊到模塊auth.coffee和模擬它,像這樣:

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

所以從摩卡測試你就可以創建一個虛擬帳戶對象,並在實際測試中興農嘲笑它。

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中,但我不習慣它。

+0

謝謝。這工作。這種方法不是劫持模塊,而是依賴注入和模仿。 –

+1

你不需要在'beforeEach'方法中實現'account.register'。簡單地使用存根,就像我做了一個調用'yield'來做異步回調。看看存根和產量的sinon doc。 – Stefan

0

斯蒂芬的解決方案的作品。我只是添加一些細節。

describe 'register', -> 
    account = response = routes_auth = null 

    beforeEach -> 
     account = 
      register: (email, pw, callback)-> 
       if email is '[email protected]' 
        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: '[email protected]' 
       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 
0

我一直在使用gently的嘲諷和測試框架磕碰和mocha,並測試BDD風格should.js。下面是我的樣本單元測試什麼樣子:

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'); 
    }); 
});