如何在 NodeJS 中测试服务器发送事件 (SSE) 路由?
How to test a Server Sent Events (SSE) route in NodeJS?
我的 NodeJS 应用程序上有一个服务器发送事件路由,客户端可以订阅该路由以从服务器获取实时更新。看起来像这样:
router.get('/updates', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
})
const triggered = (info) => {
res.write(`\ndata: ${JSON.stringify(info)}\n\n`)
}
eventEmitter.addListener(constants.events.TRIGGERED, triggered)
req.on('close', () => {
eventEmitter.removeListener(constants.events.TRIGGERED, triggered)
})
})
使用 supertest
测试传统路由在节点中非常简单:
test('Should get and render view', async() => {
const res = await request(app)
.get('/')
.expect(200)
expect(res.text).not.toBeUndefined()
})
但是,这在测试 SSE 路由时不起作用。
有没有人对如何使用 Node 测试 SSE 路由有任何想法?它不一定必须用 supertest
进行测试。只是寻找关于如何测试它的想法,supertest
或其他。
编辑:
我对如何对此进行集成测试有一个想法。基本上,人们必须在测试前启动服务器,在测试期间订阅它并在测试后关闭它。但是,当我使用 beforeEach() 和 afterEach() 启动服务器时,它在 Jest 中没有按预期工作。
我会 mock/fake 端点使用的所有内容,并检查端点是否使用正确的变量以正确的顺序执行。首先,我会在端点之外声明 trigger
函数和 close
事件回调,以便我可以直接测试它们。其次,我会消除所有函数中的所有全局引用以支持函数参数:
let triggered = (res) => (info) => {
res.write(`\ndata: ${JSON.stringify(info)}\n\n`);
}
let onCloseHandler = (eventEmitter, constants, triggered, res) => () => {
eventEmitter.removeListener(constants.events.TRIGGERED, triggered(res));
}
let updatesHandler = (eventEmitter, constants, triggered) => (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
eventEmitter.addListener(constants.events.TRIGGERED, triggered(res));
req.on('close', onCloseHandler(eventEmitter, constants, triggered, res));
};
router.get('/updates', updatesHandler(eventEmitter, constants, triggered));
使用此代码,测试用例将如下所示:
test("triggered", () => {
let res;
beforeEach(() => {
res = generateFakeRespone();
});
it("should execute res.write with the correct variable", () => {
trigger(res)("whatever");
expect(res.write).to.have.been.called.once;
expect(res.write).to.have.been.called.with(`\ndata: ${JSON.stringify("whatever")}\n\n`);
});
});
test("onCloseHandler", () => {
let res;
let eventEmitter;
let constants;
let triggered;
beforeEach(() => {
res = Math.random();
eventEmitter = generateFakeEventEmitter();
constants = generateFakeConstants();
triggered = generateFakeTriggered();
});
it("should execute eventEmitter.removeListener", () => {
onCloseHandler(eventEmitter, constants, triggered, res);
expect(eventEmitter.removeListener).to.have.been.called.once;
expect(eventEmitter.removeListener).to.have.been.called.with(/*...*/)
});
});
test("updatesHandler", () => {
beforeEach(() => {
req = generateFakeRequest();
res = generateFakeRespone();
eventEmitter = generateFakeEventEmitter();
constants = generateFakeConstants();
triggered = generateFakeTriggered();
});
it("should execute res.writeHead", () => {
updatesHandler(eventEmitter, constants, triggered)(req, res);
expect(res.writeHead).to.have.been.called.once;
expect(res.writeHead).to.have.been.called.with(/*...*/)
});
it("should execute req.on", () => {
//...
});
// more tests ...
});
有了这种编码和测试风格,您就有能力进行非常详细的单元测试。缺点是需要更多的努力才能正确测试所有内容。
查看 express-sse 库的测试。他们在端口上启动服务器,然后创建 EventSource
的实例并将其连接到 运行 服务器上的 SSE end-point。
像这样:
describe("GET /my-events", () => {
let events
let server
beforeEach(function (done) {
events = new EventEmitter()
const app = createMyApp(events)
server = app.listen(3000, done)
})
afterEach(function (done) {
server.close(done)
})
it('should send events', done => {
const es = new EventSource('http://localhost:3000/my-events')
events.emit('test', 'test message')
es.onmessage = e => {
assertThat(e.data, equalTo('test message'))
es.close()
done()
}
})
})
对我来说,这似乎是正确的测试方法。
我的 NodeJS 应用程序上有一个服务器发送事件路由,客户端可以订阅该路由以从服务器获取实时更新。看起来像这样:
router.get('/updates', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
})
const triggered = (info) => {
res.write(`\ndata: ${JSON.stringify(info)}\n\n`)
}
eventEmitter.addListener(constants.events.TRIGGERED, triggered)
req.on('close', () => {
eventEmitter.removeListener(constants.events.TRIGGERED, triggered)
})
})
使用 supertest
测试传统路由在节点中非常简单:
test('Should get and render view', async() => {
const res = await request(app)
.get('/')
.expect(200)
expect(res.text).not.toBeUndefined()
})
但是,这在测试 SSE 路由时不起作用。
有没有人对如何使用 Node 测试 SSE 路由有任何想法?它不一定必须用 supertest
进行测试。只是寻找关于如何测试它的想法,supertest
或其他。
编辑: 我对如何对此进行集成测试有一个想法。基本上,人们必须在测试前启动服务器,在测试期间订阅它并在测试后关闭它。但是,当我使用 beforeEach() 和 afterEach() 启动服务器时,它在 Jest 中没有按预期工作。
我会 mock/fake 端点使用的所有内容,并检查端点是否使用正确的变量以正确的顺序执行。首先,我会在端点之外声明 trigger
函数和 close
事件回调,以便我可以直接测试它们。其次,我会消除所有函数中的所有全局引用以支持函数参数:
let triggered = (res) => (info) => {
res.write(`\ndata: ${JSON.stringify(info)}\n\n`);
}
let onCloseHandler = (eventEmitter, constants, triggered, res) => () => {
eventEmitter.removeListener(constants.events.TRIGGERED, triggered(res));
}
let updatesHandler = (eventEmitter, constants, triggered) => (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
eventEmitter.addListener(constants.events.TRIGGERED, triggered(res));
req.on('close', onCloseHandler(eventEmitter, constants, triggered, res));
};
router.get('/updates', updatesHandler(eventEmitter, constants, triggered));
使用此代码,测试用例将如下所示:
test("triggered", () => {
let res;
beforeEach(() => {
res = generateFakeRespone();
});
it("should execute res.write with the correct variable", () => {
trigger(res)("whatever");
expect(res.write).to.have.been.called.once;
expect(res.write).to.have.been.called.with(`\ndata: ${JSON.stringify("whatever")}\n\n`);
});
});
test("onCloseHandler", () => {
let res;
let eventEmitter;
let constants;
let triggered;
beforeEach(() => {
res = Math.random();
eventEmitter = generateFakeEventEmitter();
constants = generateFakeConstants();
triggered = generateFakeTriggered();
});
it("should execute eventEmitter.removeListener", () => {
onCloseHandler(eventEmitter, constants, triggered, res);
expect(eventEmitter.removeListener).to.have.been.called.once;
expect(eventEmitter.removeListener).to.have.been.called.with(/*...*/)
});
});
test("updatesHandler", () => {
beforeEach(() => {
req = generateFakeRequest();
res = generateFakeRespone();
eventEmitter = generateFakeEventEmitter();
constants = generateFakeConstants();
triggered = generateFakeTriggered();
});
it("should execute res.writeHead", () => {
updatesHandler(eventEmitter, constants, triggered)(req, res);
expect(res.writeHead).to.have.been.called.once;
expect(res.writeHead).to.have.been.called.with(/*...*/)
});
it("should execute req.on", () => {
//...
});
// more tests ...
});
有了这种编码和测试风格,您就有能力进行非常详细的单元测试。缺点是需要更多的努力才能正确测试所有内容。
查看 express-sse 库的测试。他们在端口上启动服务器,然后创建 EventSource
的实例并将其连接到 运行 服务器上的 SSE end-point。
像这样:
describe("GET /my-events", () => {
let events
let server
beforeEach(function (done) {
events = new EventEmitter()
const app = createMyApp(events)
server = app.listen(3000, done)
})
afterEach(function (done) {
server.close(done)
})
it('should send events', done => {
const es = new EventSource('http://localhost:3000/my-events')
events.emit('test', 'test message')
es.onmessage = e => {
assertThat(e.data, equalTo('test message'))
es.close()
done()
}
})
})
对我来说,这似乎是正确的测试方法。