iT邦幫忙

2024 iThome 鐵人賽

DAY 12
1

nestjs 測試概念

目標

  1. 解說測試的目標與作用
  2. 在 nestjs 可以使用的測試工具

概念

測試的主要目的在於能夠確保開發者設計的功能能夠符合預期的邏輯。而根據範圍一般可以分為:

  1. 測試單一職責服務邏輯的單元測試
    通常需要保持快速且設計邏輯一致,為測試中最好驗證且損耗資源最小的測試。
  2. 測試多個服務互動邏輯的整合測試
    通常會整個使用者情境操作情境來做測試,相較於單元測試整合測試在於測試服務間的互動邏輯是否符合期待,因此需要比較多服務的設置。耗損資源比單元測試多。
  3. 測試端點與端點間互動邏輯的端對端測試
    通常就是會透過模擬最終端的使用者操作,直接對整個系統做測試。關鍵在於測試整個系統的回應邏輯是否符合期待。所以需要把整個系統啟動來做測試,損耗資源比整合測試更多。

在 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 提供了注入容器,讓更改邏輯,撰寫測試更具有標準化框架支援的好處,使得測試能更模組化好讀好修改。


上一篇
nestjs 中斷點設置 with vscode
下一篇
nestjs 系統分析與規劃 活動訂票管理系統
系列文
透過 nestjs 框架,讓 nodejs 系統維護度增加26
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
孤獨一隻雞
iT邦研究生 5 級 ‧ 2024-09-12 18:34:49

好 來學

0
雷N
iT邦研究生 1 級 ‧ 2024-09-12 18:44:03

好 來學https://ithelp.ithome.com.tw/upload/images/20240912/20104930FIEDVhPZ7V.png

好來學

我要留言

立即登入留言