我想我已经达到了我想要的设置:
-
await 无处不在,没有无限深度嵌套的回调
- 除了 mocha 和 express 之外没有外部库,类似的设置也适用于其他系统
- 没有嘲笑。没有嘲笑额外的努力。它只是在每次测试的随机端口上启动一个干净的服务器,并在测试结束时关闭服务器,就像真实的一样
此示例中未显示,您可能希望通过在使用 NODE_ENV=test 时使用每个测试唯一的临时内存 SQLite 数据库运行应用程序来完成以下操作。真正的生产服务器将在 PostgreSQL 之类的东西上运行,并且将使用像 sequelize 这样的 ORM,以便相同的代码在两者上运行。或者,您可以设置一次创建数据库并在每次测试之前截断所有表。
app.js
#!/usr/bin/env node
const express = require('express')
async function start(port, cb) {
const app = express()
app.get('/', (req, res) => {
res.send(`asdf`)
})
app.get('/qwer', (req, res) => {
res.send(`zxcv`)
})
return new Promise((resolve, reject) => {
const server = app.listen(port, async function() {
try {
cb && await cb(server)
} catch (e) {
reject(e)
this.close()
throw e
}
})
server.on('close', resolve)
})
}
if (require.main === module) {
start(3000, server => {
console.log('Listening on: http://localhost:' + server.address().port)
})
}
module.exports = { start }
test.js
const assert = require('assert');
const http = require('http')
const app = require('./app')
function testApp(cb) {
return app.start(0, async (server) => {
await cb(server)
server.close()
})
}
// https://stackoverflow.com/questions/6048504/synchronous-request-in-node-js/53338670#53338670
function sendJsonHttp(opts) {
return new Promise((resolve, reject) => {
try {
let body
if (opts.body) {
body = JSON.stringify(opts.body)
} else {
body = ''
}
const headers = {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
'Accept': 'application/json',
}
if (opts.token) {
headers['Authorization'] = `Token ${opts.token}`
}
const options = {
hostname: 'localhost',
port: opts.server.address().port,
path: opts.path,
method: opts.method,
headers,
}
const req = http.request(options, res => {
res.on('data', data => {
let dataString
let ret
try {
dataString = data.toString()
if (res.headers['content-type'].startsWith('application/json;')) {
ret = JSON.parse(dataString)
} else {
ret = dataString
}
resolve([res, ret])
} catch (e) {
console.error({ dataString });
reject(e)
}
})
// We need this as there is no 'data' event empty reply, e.g. a DELETE 204.
res.on('end', () => resolve([ res, undefined ]))
})
req.write(body)
req.end()
} catch (e) {
reject(e)
}
})
}
it('test root', () => {
// When an async function is used, Mocha waits for the promise to resolve
// before deciding pass/fail.
return testApp(async (server) => {
let res, data
// First request, normally a POST that changes state.
;[res, data] = await sendJsonHttp({
server,
method: 'GET',
path: '/',
body: {},
})
assert.strictEqual(res.statusCode, 200)
assert.strictEqual(data, 'asdf')
// Second request, normally a GET to check that POST.
;[res, data] = await sendJsonHttp({
server,
method: 'GET',
path: '/',
body: {},
})
assert.strictEqual(res.statusCode, 200)
assert.strictEqual(data, 'asdf')
})
})
it('test /qwer', () => {
return testApp(async (server) => {
let res, data
;[res, data] = await sendJsonHttp({
server,
method: 'GET',
path: '/qwer',
body: {},
})
assert.strictEqual(res.statusCode, 200)
assert.strictEqual(data, 'zxcv')
})
})
package.json
{
"name": "tmp",
"version": "1.0.0",
"dependencies": {
"express": "4.17.1"
},
"devDependencies": {
"mocha": "6.2.2"
},
"scripts": {
"test": "mocha test test.js"
}
}
有了这个,运行:
npm install
./app
根据需要正常运行服务器。并运行:
npm test
使测试按需要进行。值得注意的是,如果您将任何断言修改为错误的值,它们将抛出,服务器关闭而不挂起,最后失败的测试显示为失败。
异步 http 请求也在:Synchronous request in Node.js
在 Node.js 14.17.0、Ubuntu 21.10 上测试。