我使用Supertest测试我的Express应用程序,但是当我希望处理程序在发送请求后 进行异步处理时,我遇到了挑战。以下面的代码为例:
const request = require('supertest');
const express = require('express');
const app = express();
app.get('/user', async (req, res) => {
res.status(200).json({ success: true });
await someAsyncTaskThatHappensAfterTheResponse();
});
describe('A Simple Test', () => {
it('should get a valid response', () => {
return request(app)
.get('/user')
.expect(200)
.then(response => {
// Test stuff here.
});
});
});
如果someAsyncTaskThatHappensAfterTheResponse()
调用引发错误,则此处的测试会受到竞争条件的影响,在该条件下,基于该错误它可能会失败,也可能不会失败。除了错误处理外,如果在设置响应后发生副作用,也很难检查副作用。假设您要在发送响应后触发数据库更新。您无法从测试中得知何时应该期望更新已完全完成。有什么方法可以使用Supertest等待处理程序函数完成执行吗?
答案 0 :(得分:0)
要做到这一点并不容易,因为超级测试的行为就像一个客户端,并且您无权访问express中的实际req / res对象(请参见https://stackoverflow.com/a/26811414/387094)。
作为一个完整的解决方法,这对我有用。
创建一个包含回调/承诺的文件。例如,我的文件test-hack.js看起来像这样:
let callback = null
export const callbackPromise = () => new Promise((resolve) => {
callback = resolve
})
export default function callWhenComplete () {
if (callback) callback('hack complete')
}
所有处理完成后,调用回调callWhenComplete
函数。例如,我的中间件看起来像这样。
import callWhenComplete from './test-hack'
export default function middlewareIpnMyo () {
return async function route (req, res, next) {
res.status(200)
res.send()
// async logic logic
callWhenComplete()
}
}
最后在您的测试中,像这样等待callbackPromise:
import { callbackPromise } from 'test-hack'
describe('POST /someHack', () => {
it.only('should handle a post request', async () => {
const response = await request
.post('/someHack')
.send({soMuch: 'hackery'})
.expect(200)
const result = await callbackPromise()
// anything below this is executed after callWhenComplete() is
// executed from the route
})
})
答案 1 :(得分:0)
受@ travis-stevens的启发,这是使用setInterval
的稍微不同的解决方案,因此您可以确保在进行超级测试呼叫之前已设置了promise。如果您想将该库用于许多测试而不会发生冲突,这还允许按id跟踪请求。
const backgroundResult = {};
export function backgroundListener(id, ms = 1000) {
backgroundResult[id] = false;
return new Promise(resolve => {
// set up interval
const interval = setInterval(isComplete, ms);
// completion logic
function isComplete() {
if (false !== backgroundResult[id]) {
resolve(backgroundResult[id]);
delete backgroundResult[id];
clearInterval(interval);
}
}
});
}
export function backgroundComplete(id, result = true) {
if (id in backgroundResult) {
backgroundResult[id] = result;
}
}
在您的supertest.request()
呼叫之前进行呼叫以使听众答应(在这种情况下,使用代理)。
it('should respond with a 200 but background error for failed async', async function() {
const agent = supertest.agent(app);
const trackingId = 'jds934894d34kdkd';
const bgListener = background.backgroundListener(trackingId);
// post something but include tracking id
await agent
.post('/v1/user')
.field('testTrackingId', trackingId)
.field('name', 'Bob Smith')
.expect(200);
// execute the promise which waits for the completion function to run
const backgroundError = await bgListener;
// should have received an error
assert.equal(backgroundError instanceof Error, true);
});
您的控制器应该期望跟踪ID,并在控制器后台处理结束时将其传递给完整功能。传递错误作为第二个值是稍后检查结果的一种方法,但是您可以传递false或任何您喜欢的东西。
// if background task(s) were successful, promise in test will return true
backgroundComplete(testTrackingId);
// if not successful, promise in test will return this error object
backgroundComplete(testTrackingId, new Error('Failed'));
如果任何人有任何评论或改进,将不胜感激:)