測試的主要目的在於能夠確保開發者設計的功能能夠符合預期的邏輯。而根據範圍一般可以分為:
在 nestjs 中,由於有使用了依賴注入的容器,所以很方便把元件做隔離。在單元測試的部份以 jest 為基礎,提供一些測試常用檢測工具。在端對端測試的部份以 supertest 整合了 jest ,整合了依賴注入的容器,方便從端點做測試。
假設預期 CatsController 會使用到 CatService ,且 CatsController 裏面有一個 Get 的路由對應 CatsController 的 findAll 方法,會呼叫一個叫作 CatService 裏面一個 findAll 的方法,回傳會是一個 string 陣列。
則測試可以透過 nestjs test 套件撰寫如下:
// 套件引用
import { Test } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
// 測試宣告
describe('CatsController', () => {
// 初始化測試載體
let catsController: CatsController;
let catsService: CatsService;
// 在所有測試執行之前會執行區塊
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
controllers: [CatsController],
providers: [CatsService],
}).compile();
catsService = moduleRef.get<CatsService>(CatsService);
catsController = moduleRef.get<CatsController>(CatsController);
});
// 測試 CatsController findAll 行為
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);
});
});
})
如果相同例子,但這次希望測試從端點直接打入的行為。假設預期 CatsController 會使用到 CatService ,且 CatsController 裏面有一個 Get 的路由對應 CatsController 的 findAll 方法,會回應會是 200 , 並且回傳一個陣列。
// 套件引用
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({
data: catsService.findAll(),
});
});
// 測試結束 關閉測試載體
afterAll(async () => {
await app.close();
});
});
從上面的範例,可以看出來其實要先根據需求設計系統行為,這樣才能知道實作時該有什麼回應。也就是所謂『先劃靶,再射箭』。
測試在軟體開發中是讓開發者驗證設計邏輯的一種手段。當軟體需求不斷變更演進,為了能夠讓軟體邏輯符合新的規格,也必須要將測試做更改。因此,相較於其他的開發方法論,測試驅動開發這種藉由測試規格當藍圖來設計系統的方法論,比較能適應大量時常變更需求的開發情境。
先去思考系統行為,訂出系統介面,根據介面撰寫測試。首先經歷測試紅燈,確認測試有做到檢測行為。然後,做出最初步簡單實作,讓測試可以通過並成綠燈。接著重構成好讀且容易修改的邏輯結構。
nestjs 提供了注入容器,讓更改邏輯,撰寫測試更具有標準化框架支援的好處,使得測試能更模組化好讀好修改。