我想学习如何正确地对快速端点(尤其是处理程序代码)进行单元测试,并在响应中声明正确的状态和数据。
我想做这个没有超级测试,因为我有一个带有一堆中间件功能的助手库,并且我想单独测试它们。
对于这样的简单应用
'use strict'
const express = require('express')
const app = express()
const helloWorld = require('./helloWorld')
app.get('/', helloWorld)
app.listen(5000, () => console.log('we\'re up!'))
具有这样的简单处理程序功能
'use strict'
function helloWorld (req, res, next) {
const data = {
hello: 'world'
}
res.status(200).send(data)
}
module.exports = helloWorld
我正在做这个测试
'use strict'
const helloWorld = require('./helloWorld')
describe('#helloWorld', () => {
it('should return 200', () => {
const req = {
}
const res = {
status: function (code) {
this.statusCode = code
return this
},
send: function () {
return this
}
}
const next = () => {}
helloWorld(req, res, next)
// TODO: How to assert status was 200 and data sent was { hello: 'world' }?
})
})
如何断言状态200和数据为{ hello: 'world' }
?
更新 这行得通,但是如果这样行事是一个糟糕的主意,那就去idk。
更新的测试
'use strict'
const { expect } = require('chai')
const helloWorld = require('./helloWorld')
describe('#helloWorld', () => {
it('should return 200', () => {
const req = {
}
const res = {
_status: null,
_json: null,
status: function (code) {
this._status = code
return this
},
send: function (json) {
this._json = json
return this
}
}
const next = () => {}
helloWorld(req, res, next)
expect(res._status).to.equal(200)
expect(res._json.hello).to.equal('world')
})
})
答案 0 :(得分:0)
由于res.status()
和res.send()
实际上正在调用较低级别的node.js http
方法,因此实际上没有更多的内联测试可以在这里进行。多年来,这些大小写法已经在大小公司的几个大型生产环境中进行了实战测试,因此您可以放心,他们将做好自己的工作。
更有用的测试可能是确保您的服务器完全以正确的响应进行响应。您可以做的是创建一个单独的test-server.js
文件,然后使用request
库通过localhost:port
进行几次测试,以命中服务器。这将模仿客户端与服务器的交互。
const request = require('request');
const assert = require('assert');
// Example test
request('http://localhost:8080', (err, res, body) => {
// Run your tests here
assert.strictEqual(body.hello, 'world');
assert.strictEqual(res.statusCode, 200);
// .. etc
});
每次更改服务器以对其进行测试时,您就可以运行test-server.js
文件。
答案 1 :(得分:0)
您需要引入一个模拟/存根库(我个人倾向于使用Sinon)并模拟res
对象,例如
// Setup stubs
const req = {};
const res = {
status() {},
send() {}
};
const next = () => {}
// Setup mock
const resMock = sinon.mock(res);
resMock.expects('status').once().withArgs(200);
resMock.expects('send').once().withArgs({ hello: 'world' });
// Invoke code with mock
helloWorld(req, resMock, next);
// Assert expectations
resMock.verify();
您还可以将存根或间谍用于多个函数断言,尽管我发现更易于设置模拟程序。
使用间谍代替模拟的相同示例
// Setup stubs
const req = {};
const res = {
status() {},
send() {}
};
const next = () => {};
// Setup spies
const statusSpy = sinon.spy(res, 'status');
const sendSpy = sinon.spy(res, 'send');
// Invoke code
helloWorld(req, res, next);
// Assert calls
expect(statusSpy.calledOnceWith(200)).to.be.true;
expect(sendSpy.calledWithMatch({ hello: 'world' })).to.be.true;
如果这是许多测试中的普遍趋势,那么您可以设置
const req = {};
const res = {
status() {},
send() {}
};
const next = () => {};
...
before(() => {
// Setup spies once for test suite
sinon.spy(res, 'status');
sinon.spy(res, 'send');
})
it('should return 200', () => {
helloWorld(res, res, next);
expect(res.status.calledOnceWith(200)).to.be.true;
expect(res.send.calledWithMatch({ hello: 'world' })).to.be.true;
})
afterEach(() => {
// reset spies after each test
res.status.resetHistory();
res.send.resetHistory();
})