自动化测试是软件开发的基础,至少测试要覆盖到系统里的一些比较敏感的部分。我们需要使用不同类型的测试,比如集成测试、单元测试、e2e 测试等等,Nest 提供了一些工具可以改善创建这些测试的体验,默认集成了 Jest 测试框架,你也可以使用自己熟悉的测试框架。
安装
先得安装需要的包:
npm install @nestjs/testing --save-dev
单元测试
Unit testing,单元测试。Jest 是一个非常成熟的测试框架,框架可以用来运行测试,里面也提供了 assert 函数,测试替身,可以用来 mocking 或 spying 等等。
假设应用里有两个类:CatsController 与 CatsService。下面我们手工强制让调用 catsService.findAll() 方法的时候返回 result,这样我们就可以测试 catsController.findAll() 方法是否可以返回我们期望的结果了。
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
describe('CatsController', () => {
let catsController: CatsController;
let catsService: CatsService;
beforeEach(() => {
catsService = new CatsService();
catsController = new CatsController(catsService);
});
describe('findAll', () => {
it('should return an array of cats', async () => {
const result = ['test'];
jest.spyOn(catsService, 'findAll').mockImplementation(() => result);
expect(await catsController.findAll()).toBe(result);
})
});
});上面这个测试并没有用到 Nest 测试工具,测试的类是手动实例化的,这种测试叫 isolated tests(独立测试)。
测试工具
在 @nestjs/testing 里提供的工具,重新创建上面这个测试。
import { Test } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
describe('CatsController', () => {
let catsController: CatsController;
let catsService: CatsService;
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [CatsController],
providers: [CatsService],
}).compile();
catsService = module.get(CatsService);
catsController = module.get(CatsController);
});
describle('findAll', () => {
it('should return an array of cats', async () => {
const result = ['test'];
jest.spyOn(catsService, 'findAll').mockImplementation(() => result);
expect(await catsController.findAll()).toBe(result);
})
});
});Test 类里有个 createTestingModule() 方法,接收的对象参数跟 @Module 一样。这个方法创建会一个 TestingModule 实例,里面有几个方法,在做单元测试时只有一个方法有用,就是 compile() ,模块被编译之后,可以使用 get() 方法获取到任意实例。要 mock 真正的实例,可以使用自定义 provider 覆盖已有 provider。
End-to-end 测试
我们的应用会越来越复杂,手工测试应用里提供的所有接口也会越来越困难,可以用 e2e 测试帮我们去测试。
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { INestApplication } from '@nestjs/common';
describle('Cats', () => {
let app: INestApplication;
let catsService = {
findAll: () => ['test']
};
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [CatsModule],
})
.overrideProvider(CatsService)
.useValue(catsService)
.compile();
app = module.createTestingModule();
await app.init();
});
it('/GET cats', () => {
return request(app.getHttpServer())
.get('/cats')
.expect(200)
.expect({
data: catsService.findAll(),
});
});
afterAll(async () => {
await app.close();
});
});e2e 测试可以放在 e2e 目录里面,测试文件要有 .e2e-spec 或 .e2e-test 后缀。
cats.e2e-spec.ts 文件包含一个 HTTP 接口测试(/cats)。我们要用 app.getHttpServer() 方法获取到在后台运行的 Nest 应用的 HTTP 服务器。
TestingModule 实例提供了 overrideProvider() 方法,可以覆盖已有 provider ,还可以使用 overrideGuard(),overrideInterceptor(),overrideFilter() 还有 overridePipe 覆盖相应的东西。
编译的模块有下面几个方法:
- createNestApplication(),基于模块创建 Nest 实例(返回 INestApplication)。需要使用 init() 方法手工初始化应用。
- createNestMicroservice(),基于模块创建 Nest 微服务实例(返回 INestMicroservice)。
- get(),获取控制器、provider 实例。
- select(),从选择的模块里提取特定的实例。



