0%

Jest與supertest搭配Github Action單元測試及CI應用 - 第三方模組依賴解決方案

簡介 Intro

這陣子為了交付各項文件,以及為了往後系統開發的流暢,接觸了許多DevOps相關的技術

本次章節打算來說一說Jest與supertest搭配Github Action的組合應用


前言

在網路上有很多關於DevOps、CI/CD相關的呼聲,幾乎快跟雲平台、AI名詞爛大街了

但實際上能夠參考的中文文章有限,很多都只是淺嘗即止。

不然就都是全英文的文章,對於我這種野生開發者來說,實在是太吃力了。

(良心建議英文要學好RRRRRRR)

那接續上一篇關於JSDoc的文章,這次打算介紹的是Jest+supertest進行單元測試

並且介紹Github Action的配置,但整體而言內容會很多,所以這個主題可能會分多篇文章寫完

技術使用簡介

Jest

Jest是由Facebook維護的JavaScript測試框架,由Christoph Nakazawa設計和構建,重點是簡化和支持大型Web應用程序。…

介紹可以點我直接連結到維基百科去看

supertest

supertest是一個用來模擬Http Request的套件,用來測試webserver實很實用

官方文件

Github Action

雖然本次文章不會說到這個階段,但還是附上官方文件連結

官方文件


單元測試

好啦~ 由於本人精力與時間都實在有限就直接切入正題

當我們的專案隨越來越大時,所使用的套件也會越來越多

不論是自己寫的模組也好,還是引用外部的第三方模組

跟著主流的中文單元測試教學文件都會遇到一個問題

Jest在進行測試時,會因為無法正常處理axios、socket.io-client、mqtt

導致單元測試失效,要解決這個問題還得回到官方文檔

簡單來說,在Jest的測試框架環境下,我們需要使用他所提供的mock模擬一個假的元件騙自己

程式碼模擬

假設我們的routes/index.js內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const express = require("express");
const router = express.Router();

const axios = require("axios");

axios.post("URL",payload).then((response) => {
console.log(response.data);
}).catch((error)=>{
console.log(error);
});

router.get("/",async (req,res,next) => {
res.staus(200).send("hello world");
});

然後我們的example.test.js的內容為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const supertest = require("supertest");
const path = require("path");
const app = require(path.join(__dirname,"..","app.js"));
const request = supertest(app);

describe("Simple Unit Test",()=>{
test("GET /",(done) => {
request.get("/").end((err,res)=>{
if (err) {
console.log(err);
done.fail(err);
} else {
console.log(res.status); //should be 200
done(); //記得測試結束要呼叫done,失敗要呼叫done.fail(err) 阿不然你的測試不會結束然後就timeout當作失敗了
}
})
});
});

我們的package.json配置:

1
2
3
4
5
6
7
8
9
{
"scripts":{
"start": "node ./bin/www",
"test": "jest"
},
"jest":{
"testTimeout":10000
}
}

神奇的錯誤

然而當我們開開心心運行npm test時,就會跳錯 (這邊有空再附上圖片)

大概要馬告訴你timeout,不然就是說你可能promise沒結束,又或是說他不認得axios (欠扁!)

這並不是因為配置出錯,也許他會提示你使用 –detectOpenHandles 參數,但其實一點也沒解決問題

1
npm test -- --detectOpenHandles

解決辦法

解鈴還須繫鈴人

而我們需要在*.test.js中(也就是你單元測試的腳本),進行一些更動

原本的內容都保持不動,我們要使用jest.mock模擬需要的元件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const supertest = require("supertest");
const path = require("path");
const app = require(path.join(__dirname,"..","app.js"));
const request = supertest(app);

//jest.mock("axios"); 這樣的話在jest它的視角來說axios = undefined;

jest.mock("axios",()=>{//所以我們需要相對完整的模擬axios的呼叫
return {
post:()=>{
return new Promise((resolve,reject)=>{
resolve(true); //根據每個人的專案不同自行調整,反正我只是要騙jest讓它單純檢查我的webserver而已
});
}
}
});

describe("Simple Unit Test",()=>{
test("GET /",(done) => {
request.get("/").end((err,res)=>{
if (err) {
console.log(err);
done.fail(err);
} else {
console.log(res.status); //should be 200
done(); //記得測試結束要呼叫done,失敗要呼叫done.fail(err) 阿不然你的測試不會結束然後就timeout當作失敗了
}
})
});
});

成功解決問題

將一切存檔之後我們再執行一次給他一個機會

1
npm test

終於成功啦

還好成功了,不然就要開扁了(誤)

基本上這是一個比較粗糙的解決辦法,但若是要詳細說明,篇幅實在太長

往後再慢慢加入解說

順帶一提

環境變數傳遞及Jest參數簡述

當使用npm test進行測試時,可以透過– –param 傳遞環境參數

1
2
3
4
5
#Example1
npm test -- --detectOpenHandles

#Example2 執行單一個指定的單元測試腳本
npm test -- --runTestsByPath "./__test__/example.test.js"

結語

這只是CI/CD的開頭,路漫漫其修遠兮, 吾將上下而求索。

雖然過聖誕節一天了,還是祝各位開發者佳節愉悅

也歡迎各位大大指教及交流