You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Supertest测试Express POST接口时请求体undefined问题

使用Supertest测试Express POST接口时请求体参数无法传入的问题

我用Supertest测试Express服务器的POST接口,Postman调用一切正常,但测试里传递请求体参数时,参数无法正确传入,出现「request body undefined」的问题。

controller.ts 代码

import { Router } from 'express';
import { z } from 'zod';
import { Kysely } from 'kysely';
import * as sprints from './services';
import * as schema from './schema';
import { DB } from '../../database';

export function createSprintsRouter(db: Kysely<DB>) {
  const router = Router();

  router.post('/', async (req, res) => {
    try {
      const parsedInput = schema.parseNewSprintInput.parse(req.body);
      const newSprint = await sprints.createSprint(db, parsedInput);
      res.status(201).json(newSprint);
    } catch (err) {
      if (err instanceof z.ZodError) {
        res.status(400).json({ err: err.errors });
      } else {
        res.status(500).json({ err: (err as Error).message });
      }
    }
  });

  return router;
}

测试文件 controller.spec.ts 代码

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import supertest from 'supertest';
import express from 'express';
import { Kysely } from 'kysely';
import { createSprintsRouter } from '../controller';
import { getTestDbInstance } from '../../../database/db-test';
import type { DB } from '@/database/types';

describe('Sprints Controller', () => {
  let testDb: Kysely<DB>;
  let app: express.Application;

  beforeAll(async () => {
    testDb = getTestDbInstance();
    const sprintsRouter = createSprintsRouter(testDb);
    app = express().use('/sprints', sprintsRouter);

    await testDb
      .insertInto('sprints')
      .values([
        { code: 'TST1', title: 'Test Sprint 1' },
        { code: 'TST2', title: 'Test Sprint 2' },
      ])
      .execute();
  });

  afterAll(async () => {
    await testDb.destroy();
  });

  describe("POST '/' endpoint", () => {
    it('should create a new sprint', async () => {
      const newSprint = { code: 'TST3', title: 'Test Sprint 3' };

      const res = await supertest(app)
        .post('/sprints')
        .set('Content-Type', 'application/json')
        .send(newSprint);

      expect(res.status).toBe(201);
      expect(res.body).toEqual(
        expect.objectContaining({
          code: 'TST3',
          title: 'Test Sprint 3',
        })
      );
      const allSprints = await supertest(app).get('/sprints');
      expect(allSprints.body).toHaveLength(3);
    });
  });
});

主文件 app.ts 代码

import express from 'express';
import './bot/discordBot';
import messages from './modules/messages/controller';
import messageTemplates from './modules/message-templates/controller';
import db from './database/index';
import { createSprintsRouter } from './modules/sprints/controller';

const app = express();
app.use(express.json());
const sprintsRouter = createSprintsRouter(db);

app.use('/messages', messages);
app.use('/templates', messageTemplates);
app.use('/sprints', sprintsRouter);

export default app;

问题原因及解决方法

问题出在测试文件里创建的Express实例没有配置JSON解析中间件。主文件app.ts里确实用了app.use(express.json()),但测试代码里单独新建的Express实例没有添加这个中间件,导致无法解析请求体,所以req.body为undefined。

解决方法1:给测试用的Express实例添加JSON解析中间件

修改测试文件的beforeAll部分,先挂载express.json()再添加路由:

beforeAll(async () => {
  testDb = getTestDbInstance();
  const sprintsRouter = createSprintsRouter(testDb);
  app = express()
    .use(express.json())
    .use('/sprints', sprintsRouter);

  await testDb
    .insertInto('sprints')
    .values([
      { code: 'TST1', title: 'Test Sprint 1' },
      { code: 'TST2', title: 'Test Sprint 2' },
    ])
    .execute();
});

解决方法2:直接导入主文件的app实例

直接使用生产环境相同的app配置,避免重复创建实例导致的中间件缺失:

// 导入主文件的app
import app from '../../../app';

// 测试中直接使用supertest(app),无需重新创建app实例

内容的提问来源于stack exchange,提问作者KarolisG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 08:43:18