如何用Jest模拟Node.js HTTP请求测试自定义重定向处理器?
嘿,针对你用Jest测试HTTP重定向处理器遇到的两个问题,我来给你一步步梳理解决方案:
1. 构造模拟的HTTP请求
你的redirectHttpToHttps函数依赖Node.js的http.IncomingMessage(也就是req对象),但实际上你只需要模拟它的核心属性——比如url、method,如果你的逻辑需要完整URL的话,还要加上headers.host来拼接域名。
比如要模拟一个指向http://google.com/mytest的GET请求:
const mockReq = { url: '/mytest', // Node.js的req.url默认是域名后的路径部分 method: 'GET', headers: { host: 'google.com' } };
如果你的处理器里直接用req.url去匹配url_from(而不是完整URL),那去掉headers.host就行,根据你实际的逻辑调整。
2. 断言HTTP响应的状态码与响应头
对于res对象(http.ServerResponse),我们需要模拟它的writeHead和end方法——这两个是你重定向逻辑里肯定会用到的。用Jest的jest.fn()创建模拟方法,就能轻松追踪它们的调用参数。
比如:
const mockRes = { writeHead: jest.fn(), // 模拟设置状态码和响应头的方法 end: jest.fn() // 模拟结束响应的方法 };
调用处理器后,就可以用Jest的断言来验证:
writeHead是否传入了预期的状态码(比如301)和Location重定向地址end是否被调用(确认响应正常结束)
完整的测试用例示例
结合你的测试代码,修改后的test_redirect.jest.js应该是这样的(我还补了异步处理的部分,因为数据库插入是异步的,必须等它完成再测试):
const db = require('../src/db.js'); const redirect = require('../src/handle_http.js'); // 简单的错误处理回调 const handleDbError = (error) => { if (error) throw new Error(`DB Error: ${error.message}`); }; test("should redirect exact match GET request with 301", async () => { // 初始化内存数据库并插入测试数据 const dbHandler = db(':memory:'); await new Promise((resolve, reject) => { dbHandler.exec(` INSERT INTO redirect (url_from,url_to,method,http_status_code,use_in_http,exact_match) VALUES ('http://google.com/mytest','http://yahoo.com','GET',301,1,1), ('http://google.com/mytest2','http://yandex.com','GET',302,1,0), ('http://google.com?q=ikariam','http://yandex.com','GET',302,1,1), ('http://example.com/products','https://fakestoreapi.com/products','POST',308,1,1), ('http://example.net/products','https://fakestoreapi.com/products','POST',308,1,0), ('http://example.net','https://fakestoreapi.com/products','POST',308,1,0); `, (error) => { if (error) reject(error); resolve(); }); }); // 构造匹配第一条数据的模拟请求 const mockReq = { url: '/mytest', method: 'GET', headers: { host: 'google.com' } }; // 构造模拟响应对象 const mockRes = { writeHead: jest.fn(), end: jest.fn() }; // 调用重定向处理器 redirect.redirectHttpToHttps(dbHandler, mockReq, mockRes); // 断言响应符合预期 expect(mockRes.writeHead).toHaveBeenCalledWith(301, { Location: 'http://yahoo.com' }); expect(mockRes.end).toHaveBeenCalled(); }); // 再添加一个非精确匹配的POST请求测试 test("should redirect non-exact match POST request with 308", async () => { const dbHandler = db(':memory:'); await new Promise((resolve, reject) => { dbHandler.exec(` INSERT INTO redirect (url_from,url_to,method,http_status_code,use_in_http,exact_match) VALUES ('http://example.net/products','https://fakestoreapi.com/products','POST',308,1,0); `, (error) => { if (error) reject(error); resolve(); }); }); const mockReq = { url: '/products/123', // 非精确匹配/products路径 method: 'POST', headers: { host: 'example.net' } }; const mockRes = { writeHead: jest.fn(), end: jest.fn() }; redirect.redirectHttpToHttps(dbHandler, mockReq, mockRes); expect(mockRes.writeHead).toHaveBeenCalledWith(308, { Location: 'https://fakestoreapi.com/products' }); expect(mockRes.end).toHaveBeenCalled(); });
几个关键注意点
- 异步处理:数据库插入是异步操作,必须用
async/await或者done回调等待它完成,否则测试会在数据插入前执行,导致逻辑找不到匹配项。 - 测试隔离:每个测试用例最好用独立的数据库实例(你用的
:memory:刚好符合),或者在afterEach钩子中清空表,避免测试用例之间互相干扰。 - 异步处理器:如果你的
redirectHttpToHttps函数内部是异步的(比如SQL查询用了回调或Promise),记得让测试等待异步操作完成——比如把函数改成返回Promise,然后在测试里await redirect.redirectHttpToHttps(...)。
内容的提问来源于stack exchange,提问作者Dimitrios Desyllas
相关产品推荐
相关产品推荐

