[英]Mocking express-rate-limit for unit testing
我已将“express-rate-limit”作为特定端点的中间件实施到我的应用程序中。 谈到单元测试时,我发现在尝试存根这个中间件以适当地控制响应时遇到了麻烦。
例如,我有一个每 15 分钟允许 3 个请求的路由,但是,我对该路由进行了 10 个单元测试。 前 3 个测试按预期通过,接下来的 7 个测试返回“429 Too Many Requests”响应。
“express-rate-limit”库似乎被强烈建议用于速率限制,但是,我找不到任何关于如何在测试环境中使用它的信息。
下面显示了简化的实现尝试。
速率限制.js
exports.createUser = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minute
max: 3,
message: 'Too many requests',
statusCode: 429
})
路由.js
const rateLimits = require('./ratelimit')
let incorrect = function (res, msg) {
res.status(401)
res.send({
status: 'err',
payload: {
msg: msg
}
})
}
router.post('/create', rateLimits.createUser, (req, res) => {
if (!req.body.email) return incorrect(res, 'Email not provided')
userController
.createUser(req.body.email)
.then(user => {
if (!user) incorrect(res, 'User not created')
else correct(res)
})
.catch(() => incorrect(res, 'Internal Error'))
})
测试.js
这应该将速率限制存根以仅允许发出单个请求,这意味着第二个测试应该失败。 然而,第二次测试将通过,因为它从未被调用(根据“真实”实现,速率限制仍然是 3)
const rateLimits = require('./ratelimit')
const userController = require('./usercontroller')
let server
let limiter
describe('Users', () => {
before(() => {
limiter = sinon.stub(rateLimits, 'createUser').callsFake(() => rateLimit({
windowMs: 15 * 60 * 1000, // 15 minute
max: 1,
message: 'Too many requests',
statusCode: 429
}))
server = require('../app')
})
it('should return an error for invalid email', done => {
chai.request(server)
.post('/users/create')
.send({
email: 'notanemail'
})
.end((err, res) => {
res.should.have.status(401)
res.body.status.should.equal('err')
done()
})
})
it('should return an error for missing parameter', done => {
chai.request(server)
.post('/users/create')
.send({})
.end((err, res) => {
res.should.have.status(401)
res.body.status.should.equal('err')
done()
})
})
})
这个问题Stubbing Out Middleware强调应用程序本身应该在执行存根后初始化,以确保正确加载。 这似乎不起作用,在文件顶部创建导入的 standard.implementation 也不起作用。
或者,出于测试目的,我尝试在执行时将一些文本记录到控制台以确保调用存根 function。 function 似乎永远不会被调用,因为日志永远不会打印到控制台。
limiter = sinon.stub(rateLimits, 'createUser').callsFake(() => console.log("I was executed"))
我尝试过的另一个失败的选择是直接调用express-rate-limit模块
const rateLimit = require('express-rate-limit')
limiter = sinon.stub(rateLimit.prototype, 'constructor').callsFake(() => rateLimit({
windowMs: 15 * 60 * 1000, // 15 minute
max: 1,
message: 'Too many requests',
statusCode: 429
}))
这个问题已经有 4 个月了,还没有收到回复,所以我会尽我所能解释它,因为我已经完成并让它工作了。
所以说你正在使用 express-rate-limit 像这样:
中间件.js
const rateLimit = require('express-rate-limit')
const limiter = rateLimit({
windowMs: 60 * 60 * 1000 * 24, // 24 hour window
max: 5, // start blocking after 5 requests
message:
'Too many requests sent from this IP, please try again after an hour'
})
module.exports = {
limiter
}
应用程序.js
const middleware = require('./middleware')
router.post('/my-rate-limited-api', middleware.limiter, async (req, res) => {
try {
const info = await doSomething('my args', req.body)
res.json(info)
} catch (err) {
res.status(err.status || 400).send(`error: ${err.message}`)
}
})
为了能够测试这条路线里面的东西,你需要存根限制器。
在您的测试文件中,确保在请求中包含 IP。 在你的回应中,存根
req = {
method: 'POST',
ip: '12.12.123.123',
url: '/my-rate-limited-api',
body: [{ id: '123' }]
}
和
res.headers = {}
和
res.setHeader = (x, y) => { res.headers[x] = y }
beforeEach(function () {
const middleware = require('./middleware')
this.sinon.stub(middleware, 'limiter').callsFake(function (req, res, next) {
return next()
})
res = new Promise(function (resolve, reject) {
doneResolve = this.resolve = resolve
doneReject = this.reject = reject
})
res.headers = {}
res.setHeader = (x, y) => { res.headers[x] = y }
req = {
method: 'POST',
ip: '12.12.123.123',
url: '/my-rate-limited-api',
body: [{ id: '123' }]
}
next = this.sinon.spy(function createDeferredNext () {
let resolve
const promise = new Promise(function () {
resolve = arguments[0]
})
const fn = (arg) => resolve(arg)
fn.then = promise.then.bind(promise)
return fn
})
})
从那里,您将能够根据自己的喜好测试 function。
it('can test without rate limits', function () {
router(req, res, next)
return res
.then(ret => {
expect(res.statusCode).to.eql(200)
})
})
最好的方法是检查您的应用程序是否在测试环境中运行,并对max
参数应用一些条件检查逻辑。 假设你的是每 15 分钟 5 个请求 window。你应该将 max 键设置为高于或等于测试环境中的请求数的值。 在这种情况下,我为测试环境使用了一个更大的数字,即 100。
export const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: process.env.NODE_ENV === "test" ? 100 : 3, // 3 requests per window
onLimitReached: function (req, res /*, next*/) {
throw new TooManyRequestsError();
},
handler: function (req, res /*, next*/) {
throw new TooManyRequestsError();
},
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.