在 Node.js 中模拟模块进行单元测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15790505/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mocking modules in Node.js for unit testing
提问by Arne Jenssen
I want to unit test some functions in a node.js module. I think that mocking a 3rd module would be helpful. In particular to avoid hitting the database
我想对 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
This is the module that i want to test
这是我要测试的模块
# 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
I want to be able to test that hitting this url with the correct body in the post calls the account.registerfunction but i don't want the test to hit the database. I may not have implemented the account module yet.
我希望能够测试在帖子中使用正确的正文点击此 url 会调用该account.register函数,但我不希望测试访问数据库。我可能还没有实现帐户模块。
The jasmine spec # specs/auth.test.coffee describe 'signup', ->
茉莉花规范#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: '[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()
I have looked into several mocking libraries for node.js (https://github.com/easternbloc/Syringe, https://github.com/felixge/node-sandboxed-module) but so far no success. Whatever i try in the spec, the account.registeralways get executed. Is this whole approach flawed?
我已经研究了 node.js 的几个模拟库(https://github.com/easternbloc/Syringe,https://github.com/felixge/node-sandboxed-module)但到目前为止没有成功。无论我在规范中尝试什么,account.register总是会被执行。这整个方法有缺陷吗?
采纳答案by Stefan
I am using mochaas the test framework and sinonfor mocking, stubing and spying. I would suggest you delegate your account module to the auth.coffee module and mock it like so:
我使用mocha作为测试框架,使用sinon进行模拟、存根和监视。我建议您将您的帐户模块委托给 auth.coffee 模块并像这样模拟它:
exports.init = function (account) {
// set account object
}
so from the mocha test you can then create a dummy account object and mock it with sinon in the actual test.
因此,从 mocha 测试中,您可以创建一个虚拟帐户对象,并在实际测试中用 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();
});
});
Sorry for not writing it down in coffescript but I am not used to it.
很抱歉没有用 coffescript 写下来,但我不习惯。
回答by Lautaro Ramos
I recommend proxyquire.
我推荐proxyquire。
It does what you want to achieve, while not relying on dependency injection, which is obtrusive for your code and requires code changes if you didn't write your modules in that fashion.
它完成您想要实现的目标,同时不依赖于依赖注入,这对您的代码来说是突兀的,如果您没有以这种方式编写模块,则需要更改代码。
回答by Arne Jenssen
Stefan's solution works. I just add some details.
Stefan 的解决方案有效。我只是补充一些细节。
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()
and the routes/auth.coffeemodule ...
和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
回答by George Ivanov
I've been using gentlyfor mocking and stubbing and mochafor test framework, and should.js for BDD style of tests. Here is how a sample unit test for me look like:
我一直温和地使用 mocking 和 stubbing,mocha用于测试框架,should.js 用于 BDD 风格的测试。这是我的示例单元测试的样子:
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');
});
});

