NestJS 测试

55 阅读2分钟

自动化测试是成熟软件产品的重要组成部分。对于覆盖系统中关键的部分是极其重要的。自动化测试使开发过程中的重复独立测试或单元测试变得快捷。这有助于保证发布的质量和性能。在关键开发周期例如源码检入,特征集成和版本管理中使用自动化测试有助于提高覆盖率以及提高开发人员生产力。

安装

$ npm i --save-dev @nestjs/testing

单元测试

在下面的例子中,我们有两个不同的类,分别是 CatsController 和 CatsService 。如前所述,Jest被用作一个完整的测试框架。该框架是test runner, 并提供断言函数和提升测试实用工具,以帮助 mocking,spying 等。以下示例中,我们手动实例化这些类,并保证控制器和服务满足他们的API接口。测试文件必须以 .spec 或 .test 结尾。

cats.controller.spec.ts 无测试工具

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);
   });
 });
});

测试工具

@nestjs/testing 包给了我们一套提升测试过程的实用工具。让我们重写前面的例子,但现在使用内置的 Test 类。

cats.controller.spec.ts

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 moduleRef = await Test.createTestingModule({
        controllers: [CatsController],
        providers: [CatsService],
      }).compile();

    catsService = moduleRef.get<CatsService>(CatsService);
    catsController = moduleRef.get<CatsController>(CatsController);
  });

  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);
    });
  });
});
pnpm run test

Test类提供应用上下文以模拟整个Nest运行时,这一点很有用。 Test 类有一个 createTestingModule() 方法,该方法将模块的元数据(与在 @Module() 装饰器中传递的对象相同的对象)作为参数。

端到端测试(E2E)

与重点在控制单独模块和类的单元测试不同,端对端测试在更聚合的层面覆盖了类和模块的交互——和生产环境下终端用户类似。当应用程序代码变多时,很难手动测试每个 API 端点的行为。端到端测试帮助我们确保一切工作正常并符合项目要求。为了执行 e2e 测试,我们使用与单元测试相同的配置,但另外我们使用supertest模拟 HTTP 请求。如下:cats.e2e-spec.ts

// test/cats.e2e-spec.ts
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { CatsModule } from '../src/cats/cats.module';
import { CatsService } from '../src/cats/cats.service';
import { INestApplication } from '@nestjs/common';

describe('Cats', () => {
  let app: INestApplication;
  let catsService = { findAll: () => ['test'] };

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [CatsModule],
    })
      .overrideProvider(CatsService)
      .useValue(catsService)
      .compile();

    app = moduleRef.createNestApplication();
    await app.init();
  });

  it(`/GET cats`, () => {
    return request(app.getHttpServer())
      .get('/cats')
      .expect(200)
      .expect(catsService.findAll());
  });

  afterAll(async () => {
    await app.close();
  });
});

cats/cats.controller.ts,cats/cats.service.ts,cats/cats.module.ts

import { Controller, Get } from '@nestjs/common';
import { CatsService } from './cats.service';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}
  @Get()
  getHello() {
    return this.catsService.findAll();
  }
}
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService]
})
export class CatsModule {}
import { Injectable } from '@nestjs/common';

@Injectable()
export class CatsService {
  findAll() {
    return ['test123'];
  }
}
pnpm run test:e2e