创建日期:2026-09-08 | 最近更新:2026-09-08 基于 NestJS 12 本机实测:Nest 12 脚手架单测用 vitest(
@nestjs/testing+ vitest 4),下方单测/e2e 输出均为真实运行结果;Docker 部分未在本机跑(无容器环境),为标准多阶段写法。
测试与部署:把 Nest 服务调到「敢上线」
一句话:Nest 的依赖注入既是架构优点,也是测试利器——
@nestjs/testing的TestingModule让你在测试里任意「换实现」,单测测逻辑、e2e 测整条 HTTP 链路;部署则不过「nest build出 JS + 容器跑node dist/main」两步。
1. 测试三层定位
| 层 | 测什么 | 用什么 |
|---|---|---|
| 单元测试 | 单个 service/pipe/guard 的逻辑 | TestingModule + 把依赖 mock 掉 |
| e2e 测试 | 整条「HTTP → 路由 → DTO → service」 | supertest 对着真实 app 发请求 |
| 契约/集成 | 与外部 DB/第三方 | 常用 Testcontainers / 真库的集成测试 |
Nest 12 脚手架已配好:单测
npm run test(vitest)、e2enpm run test:e2e(vitest.config.e2e.ts)。老教程里jest的写法在新脚手架被 vitest 取代——API 概念通用,跑之前看下你工程的配置文件。
2. 单元测试:TestingModule + mock 依赖
Nest 测试的核心是「不起完整应用,只编译你要测的那一小块 DI 图」:
// cats.service.spec.ts
import { Test } from '@nestjs/testing';
import { CatsService } from './cats.service.js';
import { getRepositoryToken } from '@nestjs/typeorm'; // 若用 TypeORM
describe('CatsService', () => {
let service: CatsService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
CatsService,
{
provide: getRepositoryToken(Cat), // 把 Repository 换成内存 fake
useValue: { find: () => [{ id: 1, name: 'Tom' }], save: jest.fn?.() ?? vi.fn() },
},
],
}).compile();
service = moduleRef.get(CatsService);
});
it('findAll 返回列表', async () => {
await expect(service.findAll()).resolves.toHaveLength(1);
});
});
要点:
Test.createTestingModule({ providers })不用真连 DB / 真起 Redis——把外部依赖(Repository、HttpService、Redis client)用useValue注入假实现,测的就是你自己的逻辑;.overrideProvider(X).useValue(fake)/.overrideGuard(...)也是常用姿势,适合「测 controller 时把 service mock 掉」;- 想测「Guard / Pipe 的独立逻辑」,直接
new RolesGuard(reflector)单测即可。
脚手架自带的单测(src/app.controller.spec.ts)真实运行输出:
> npm run test
> vitest run
RUN v4.1.11 /private/tmp/nestjs-guide
Test Files 1 passed (1)
Tests 1 passed (1)
Duration 641ms
3. e2e 测试:对着真实 app 打 HTTP
e2e 是「把整条链路(含 ValidationPipe、Guard)真实走一遍」:
// test/cats.e2e-spec.ts
import { Test } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import request from 'supertest'; // 或 import * as request
import { AppModule } from '../src/app.module.js';
describe('CatsController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => { await app.close(); });
it('POST /cats 带非法参数 → 400', async () => {
await request(app.getHttpServer())
.post('/cats').send({ name: 'Tom', age: 99 })
.expect(400);
});
it('GET / → Hello World!', async () => {
await request(app.getHttpServer()).get('/').expect(200, 'Hello World!');
});
});
脚手架自带 e2e(test/app.e2e-spec.ts)真实运行输出:
> npm run test:e2e
> vitest run --config ./vitest.config.e2e.ts
RUN v4.1.11 /private/tmp/nestjs-guide
Test Files 1 passed (1)
Tests 1 passed (1)
Duration 877ms
e2e 最大的价值是把 §3 的 DTO 白名单、§4 的 Guard 401 一起锁进测试——以后谁改了校验/鉴权,CI 立刻红。
4. 生产构建与运行
npm run build # nest build → 编译到 dist/
npm run start:prod # node dist/main —— 就是脚手架给的生产启动脚本
产物就是 dist/ 下一堆纯 JS + 你的 node_modules 运行时依赖。部署只要把这两样搬到服务器/镜像里。
Docker 多阶段(标准姿势,未在本机跑)
# ---- build 阶段 ----
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- run 阶段:只要产物 + 生产依赖 ----
FROM node:24-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/main.js"]
关键:run 镜像不装 devDependencies、不带源码,只带 dist + 生产依赖,镜像小、攻击面小。
配置与环境
- 别把 Key 打进镜像:用环境变量(
process.env)+@nestjs/config的ConfigModule.forRoot({ isGlobal: true })读.env; - 容器里设
PORT即可被dist/main.js读取(process.env.PORT ?? 3000); - 上健康检查:
@nestjs/terminus暴露/health(对应 Docker/K8s 的 liveness)。
5. 上线前 checklist
npm run build干净通过;npm run test+test:e2e全绿(把鉴权/校验用例写进去);npm run lint(脚手架用 oxlint)无错误;- DB 迁移用 migration 而不是 synchronize,部署前先跑迁移;
- Secret 全部走环境变量,
NODE_ENV=production; - 容器只带
dist+ 生产依赖,配/health健康检查与优雅停机(enableShutdownHooks,呼应篇 1)。
小结:Nest 一个「域」的完整姿态
Module(组装)
→ Controller(薄壳,只做路由)
→ DTO + ValidationPipe(参数进门前校验)
→ Guard(鉴权/权限,进门前拦截)
→ Service(业务,可注入 Repository / Prisma / 其它域)
→ 测试:单测 mock 依赖 + e2e 打真实 HTTP
→ 部署:nest build → dist → Docker(node dist/main) + env + health
关联
- 全系列:NestJS 入门指南 | 模块化与 Provider | 数据层 | DTO/ValidationPipe | 鉴权
- 参考:Nest Testing、CLI 与生产部署