Jest测试Node-postgres错误处理中间件遇undefined问题排查
404路径测试失败:body.message返回undefined的排查
问题背景
刚接触Jest错误处理测试,基于鸟类PostgreSQL数据库开发接口,测试未匹配路径的404响应时,测试用例因body.message返回undefined而失败。
测试代码(app.test.js)
const request = require("supertest") const db = require("../db/index.js") const {wing_shapeData,birdsOrdersData,birdsFamiliesData,birdsData,birdwatchersData,rookeryTourData,watcherTourData} = require("../db/data/test-data/index.js") const seed = require("../db/seed.js") const app = require("../app.js") beforeEach(() => seed({wing_shapeData,birdsOrdersData,birdsFamiliesData,birdsData,birdwatchersData,rookeryTourData,watcherTourData})) afterAll(() => db.end()) describe("Endpoint(path) is not recognized and cannot be found for rookery",()=>{ it("Should return status code 404 when passed bad path",()=>{ return request(app).get('/api/fjrofjiro303gjvn200').expect(404).then(({ body }) => { expect(body.msg).toBe('HTTP 404 error: path cannot be found'); }); }) })
终端错误输出
__tests__/app.test.js Endpoint(path) is not recognized and cannot be found for rookery ✕ Should return status code 404 when passed bad path (96 ms) ● Endpoint(path) is not recognized and cannot be found for rookery › Should return status code 404 when passed bad path expect(received).toBe(expected) // Object.is equality Expected: "HTTP 404 error: path cannot be found" Received: undefined 14 | it("Should return status code 404 when passed bad path",()=>{ 15 | return request(app).get('/api/fjrofjiro303gjvn200').expect(404).then(({ body }) => { > 16 | expect(body.message).toBe('HTTP 404 error: path cannot be found'); | ^ 17 | }); 18 | }) 19 | }) at toBe (__tests__/app.test.js:16:34) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 0 total Time: 0.337 s, estimated 1 s Ran all test suites matching /app.test.js/i.
相关业务代码
app.js
const express = require('express'); const app = express(); app.use(express.json()) app.use((req, res, next) => { console.log('Request method: ', req.method); console.log('Request url: ', req.url); let currentdate = new Date(); let datetime = `Date and time request method made(format:day/month/year|hours:minutes:seconds): ${currentdate.getDate()}/${(currentdate.getMonth()+1)}/${currentdate.getFullYear()}|${currentdate.getHours()}:${currentdate.getMinutes()}:${currentdate.getSeconds()}` console.log(datetime) next() }); const errorLogger = (err,req,res,next) =>{ console.log(`Error : ${err.message}`) next(err) } const handleNotFound = (req,res,next)=>{ res.status(404).send({message: 'HTTP 404 error: path cannot be found'}) next() } const errorResponder = (err,req,res,next) =>{ if(err.statusCode && err.message){ res.status(err.statusCode).send({message: err.message}) } next(err) } const handleServerError = (err, req,res,next) => { res.status(500).send(err.message) } app.use(errorLogger) app.use(handleNotFound) app.use(errorResponder) app.use(handleServerError) app.get('/api/birds',getAllBirds)
birds.controller.js
exports.getAllBirds = (req,res,next) =>{ const {order1} = req.query const {order2} = req.query const {sort_byfirst} = req.query const {sort_bysecond} = req.query const {diet} = req.query selectAllBirds(sort_byfirst,order1,diet,sort_bysecond,order2).then((birds)=> res.status(200).send({birds})).catch(next) }
birds.models.js
const db = require('../db/index.js') exports.selectAllBirds = (sort_byfirst,order1,diet,sort_bysecond,order2) =>{ const sortbyGreenList1 = ['length_cm','weight_g','wingspan_cm'] const sortbyGreenList2 = ['common_name'] const orderbyGreenList = ['ASC','DESC'] let queryValues = []; let queryStr = `SELECT bird_id,common_name,species_name,wing_colour,diet,can_Fly ,length_cm,weight_g,lay_season,fun_fact,wingspan_cm,f_id FROM birds INNER JOIN bird_families ON birds.f_id = bird_families.family_id`; if(sort_byfirst && !sortbyGreenList1.includes(sort_byfirst)){ return Promise.reject({status: 400, message: 'Invalid sort_byfirst query string'}) } if(sort_bysecond && !sortbyGreenList2.includes(sort_bysecond)){ return Promise.reject({status: 400, message: 'Invalid sort_bysecond query string'}) } if(order1 && !orderbyGreenList.includes(order1.toUpperCase())){ return Promise.reject({status: 400, message: 'Invalid order1 query string'}) } if(order2 && !orderbyGreenList.includes(order2.toUpperCase())){ return Promise.reject({status: 400, message: 'Invalid order2 query string'}) } if(diet){ queryValues.push(`%${diet}%`) queryStr += ` WHERE diet ILIKE $1`; } if(sort_byfirst){ queryStr += ` ORDER BY ${sort_byfirst}`; } if(order1 && sort_byfirst){ queryStr += ` ${order1.toUpperCase()}`; } if(order2 && sort_bysecond && sort_byfirst){ queryStr += `, ${sort_bysecond} ${order2.toUpperCase()}`; } return db.query(queryStr,queryValues).then((result)=>{ if(!(result.rows.length)){ return Promise.reject({status: 404, err: 'No results found for query'}) }else{ return result.rows } }) }
问题原因分析
- 中间件顺序错误:
handleNotFound是处理未匹配路由的中间件,必须放在所有路由定义之后。当前它在app.get('/api/birds')之前,导致所有请求(包括合法路由)都会先进入这个中间件,发送404响应后还调用next(),后续中间件会继续处理,可能导致响应被覆盖或异常。 - 断言字段不匹配:测试代码里写的是
expect(body.msg).toBe(...),但实际响应返回的是{message: ...},错误信息里显示你又把断言改成了expect(body.message),字段名不一致同时中间件顺序错误导致响应未正确返回。 - 不必要的
next()调用:handleNotFound里发送响应后调用next()是错误的,响应已经发送完毕,再传递给下一个中间件会导致Express报错,破坏响应结构。
修复方案
1. 调整中间件顺序
把路由定义放在错误处理中间件之前,handleNotFound放在最后:
// 先定义所有路由 app.get('/api/birds',getAllBirds) // 再挂载错误处理中间件 app.use(errorLogger) app.use(handleNotFound) app.use(errorResponder) app.use(handleServerError)
2. 修正断言与响应字段的一致性
要么修改测试代码的断言:
expect(body.message).toBe('HTTP 404 error: path cannot be found');
要么修改handleNotFound的返回字段:
res.status(404).send({msg: 'HTTP 404 error: path cannot be found'})
3. 移除handleNotFound中的next()
const handleNotFound = (req,res,next)=>{ res.status(404).send({message: 'HTTP 404 error: path cannot be found'}) // 移除next(),响应已发送,无需继续传递 }
内容的提问来源于stack exchange,提问作者RendezYT
相关产品推荐
相关产品推荐

