Postman 駭客 —新手必看! 利用 Postman 撰寫 API 腳本與認識進階功能

SleeperShark
22 min readJan 17, 2023

--

最近在寫一份關於 Django 專案,需要灌進一些假資料來測試我的 GET feature,但整個資料的關聯比較複雜,我又不太會寫 SQL Script 🥲,剛好之前有稍微看過 Postman 的自動化測試功能,乾脆來用這次機會來練習用他的 test script 作為 seeder 好了。

如果你對 Postman 的應用止步於輸入 url 測試 Request、建立 folder 分門別類整理 App Requests 的話,在這篇文章中你會進一步認識 Postman 好用的進階功能,包含:

  • URL 的 Parameters 管理
  • variables
    包含 variables, collectionVariables, environmentVariable
  • Authentication 繼承
  • Test Script 撰寫與 Run Collection 的流程
  • Postman Dynamic variables

相信認識到這些功能,一定會對你的 API 測試功力大幅提升。

情境

我這次想模擬的是一個用戶填寫課堂問卷的情境,填寫的課堂週數自訂 (如:第1週到第5週)。

整個流程是:

A. 用戶登入

URL: [POST] /user/login

Request Body:

{
"email": "example@example.com",
"password": "password"
}

Response:

{
"data": {
"user" {
// user profile info
},
"token": "a-bunch-of-hash-token-code"
}
}

取得 API key,作為後面 Request 的驗證依據。

B. 取得該週數問卷的題目 id

URL: [GET] /questionnaire/question/lecture-feedback/:course_name?week=N
path parameter: course_name 課程名稱;
query string: week,填入想要取得問卷的週數

Response:

{
"data": [
{
"questionnairequestion_id": 15, // 題目id
"question": {} // 題目詳細資料
},
{
"questionnairequestion_id": 16,
"question": {}
},
]
}

questionairequestion_id 就是我要收集的資料,用做下一次提交問卷的依據。

C. 針對題目 id ,回答問題

URL: [POST] /questionnaire/answers/lecture-feedback/:course_name?week=N
path parameter: course_name 課程名稱
query string: week問卷的週數

Request Body:

{
"answers": [
{
"questionnairequestion_id": 15,
"body": "上的很棒,很滿意"
},
{
"questionnairequestion_id": 16,
"body": "有點太難"
}
]
}

從 步驟B 取得的問題 id 作為 步驟C 的Request Body 資料,並提交回答。

由於每個用戶要回答複數個週數的問卷,如 1–5 週共五份問卷,分別會提供兩個參數:startAt (1) 和 endAt (5)。
在步驟 C Request 結束後,如果 week 數字是小於 endAt 的話,那就把 week 數 +1 後再回到步驟 B,取得下一份的 題目id,以此類推。

等這個用戶填完他該填的問卷後,就在換一組帳密,回到步驟A,開啟新一輪的填答。

接下來,就簡單介紹一下我在整個 Test Script Seeder 中運用到的技術。

Tip 1. URL 的變數管理

以步驟 B 為例,如果 hard code 整個網址的話
http://localhost/questionnaire/answers/lecture/2021Spring?week=1

Postman 會自動偵測到 query string 的部分,在下面 show 出query string 的form;但 course_name (2021Spring) 的部分則因為是 pure string,Postman 不會知道這是 param。

只要在 URL 上用 : 前綴,就可以告知 Postman 這是 Path variables,在底呈現表格,協助你管理 path param。
http://localhost/questionnaire/answers/lecture/:course_name?week=1

有了表格以後,你就可以在這裡填寫相對應的 value,不需要改上面的網址,比較不容易改錯網址細節,旁邊的 description 也可以補充一些關於這個變數的資訊。

Tip 2. Postman 的 variable 應用

如果你有很多個 Request 要管理,相信你一定對於每次都要重新輸入一模一樣的 domain 這件事很煩躁。

此時就要利用 variable,將整個專案裡常常重複出現或是需要共用的參數儲存起來。

Postman 的 Variable 採用 key-value 的形式存取,根據作用域範圍 (scope) 共有四種 variable ,分別是 global / environment / collection 和 variables。

Global Variable

全域,整個 Postman Workspace 共用的變數,基本上不太常用。
點擊左側 Environments-> Global 就可以存取設定

(變數的一些細節這邊就不說明了,基本上把 Viriable 名稱跟 Current Value 填上即可)

(PS: cmd+\ 可以快速展開/收起 左側的功能欄)

Environment Variable

環境變數,在左側 Environments 、按下 Global 左上方的 + 後,就可以新增一個環境頁面。

比如說,最常重複的部分肯定是 domain 了,那這邊就可以設定 key 為 local-host,value 為 http://localhost;
設定完成以後,記得要把右上角的環境下拉選單,切換到你要的環境名稱,不然系統不知道要去哪裡找你設定的變數歐!

( 基本上這個功能就是 .env 的概念,切換選單就是你要選哪一個 .env 檔匯入環境 )

記得執行 Request 時一定要切換環境歐!

選完選單後,點擊 env 選單右邊的 Environment Quick Look 按鈕,可以瀏覽你現在的環境變數有哪些。

右上角點擊 Environment Quick Lock 確認目前可用的環境變數

Collection variables

顧名思義,是屬於 Collection 內的 Requests 共用的變數,不同的 collection 的變數彼此獨立。

點擊目前的 collection,就會看到右邊的 Variables 分頁:

Runtime variable

這是四個變數中唯一沒有介面可以操作的變數,生命週期只有“執行單次 Request” 或是 “執行一整個 Collection Runner” 之內,執行結束後就會移除。
存取方式留到 test script 說明。

這四種 variables 的優先順序是
Runtime > Collection > Environment > Global
如果變數名稱有撞名時,以上面的順序排列為依據,至於該把變數存在哪一個階層就按個人習慣決定了。

Tip 3. 取用 variables 與 Dynamic Variables

設定好變數以後,基本上在 Request 中 “任合可以輸入資訊的地方”,都可以用 {{var_name}} 的方式呼叫變數,如 URL、Parameter、QueryString、Request Body、Header、Authorization 都可以帶入。

( 👉 登入後自動帶入 access token 的例子可以看這裡)

比如說剛剛設定的 local domain,往後就可以在網址裡輸入
{{local-host}}/user/login
系統在執行時就會自動把你存取的 value 帶入變數中。把鼠標 hover 到變數上可以看到變數的詳細資訊:

綠色 icon E 代表是 Environment 變數

除了你自己設定的變數外,Postman 還有提供預設的 random variables ,叫做 dynamic variables,協助你無腦灌一些假資料,有 email、first_name、last_name、company、datetime、等等的,可以讓後台資料看起來隨機一點,比較真實 XD。

我印象最深的就是當初在測試註冊系統的時候,email 因為有 uniquu 限制,還一直在掰新的 email 出來,搞了半天只要 {{$randomEmail}} 就搞定惹 0.0。

Tip 4. Authentication

基本的 Authentication 設定在每個 Request 頁面的 Authorization tab 設定你的 access token 即可,也可以配合上面提到的 variables 設定自動帶入 access token 小技巧。

比較值得注意的是,除了 Request 以外,Collection 與 Folder 也可以個別設定 Authorization,而裡面的個別 Request 就可以在 Authorization 的下拉選單中選擇 “Inherit auth from parent” 這個選項,來取得他們的 Auth 設定。

舉例來說,你可以在 Collection 裡建立兩個 Folder: Admin 和 user, admin folder 裡放的是管理權限相關的 API,auth 則是 admin 專用的 token,而在其下的 Request 一律選擇繼承,而 user folder 同理,就可以把管理者權限與一般使用者的權限分開進行自動測試了。

在 admin folder 設定 Authorization

Tip 5. Test Script

Postman 的 Test Script 以 JavaScript 腳本執行,分為兩種,一種是 Pre-request Script,在 API Request 執行前運行,可作為 API data 的提供者;
另一種則是 Tests Script,在 API 執行後運行,作為 API 結果測試或是提取 API data 作為環境變數的功能。

Postman 很貼心的在 Script tab 的右側提供了 code snippet,依據情境提供你現成的程式碼使用,比如説設定變數、取得變數、測試 status code、測試 json 等等,使用者可以根據自己的需求來找對應的程式碼,非常方便。

測試 json 內容的 test script

如果想在 Script 中調用 dynamic variables 的功能,則要透過
pm.variables.replaceIn(“{{$randomVar}}”);
這個函數來取用。

另外, Collection、Folder 層級也有 Pre-Request Script 跟 Test 可以用,配合下一個強大的功能:Collection Runner。

Tip 6. Collection Runner

Postman 作為自動化測試工具,Collection Runner (以下簡稱 CR) 就是配合一整套相關聯的 Request 組合測試的功能,來檢查你的 Server 是否有正常的功能回應。

點擊 Collection tab 右邊的 view more actions,按下 Run Collection ,就可以看到 CR 的執行介面。

CR 的左側可以一覽所有的 API Request,預設會根據你在 Collection 擺放位置順序依次執行,你也可以在這裡透過勾選或是上下拖拉改變要測試的 Request 對象與順序。

右側則有一些額外設定,這裡依據自己的需求選擇即可,比如說要不要保存每個 Response 詳細內容,選項上都有詳細說明這裡就不細講了。

同時,每個執行的 Request 根據自己所在的位置,還會執行 Collection 和 Folder 的 Pre-Test 和 Test,因此,單一個 Request 在執行的過程共會經歷以下階段:

Collection Pre-Test => Folder Pre-Test => Request Pre-Test => Request => Request Test => Folder Test => Collection Test

根據你的需求,整理需要的 collection / folder / request Test,來最優化整個測試流程。

CR 還有一個很讚的功能,就是透過 import file 輸入資料,可以輸入 csv 或是 json 檔案,輸入後的 data 會以 Runtime variables scope 層級存在在這次的 CR 中,根據你在 csv 檔中的取名調用 {{name}} 即可。

最後一個 Tip 則是這次整個 API Script 的關鍵:Loop Request。

Tip 7. Loop Request

根據我這次的需求,我必須重複根據週數 week 取得問卷 id、根據 id 提交問卷 answer 的過程,單靠原本正常的 CR 流程是無反辦到這件事的。

Postman 在Test Script 提供了一個函數:postman.setNextRequest(‘RequestName’) ,可以直接控制下一個 Request 的對象,進而達成流程控制的目的。

有了這個函數工具以後,你就可以無視 CR 左邊的 Request 排列順序,自由的設定 Request 流程,也可以在 CR 裡執行 LooP Request,只是要小心不要寫成 Infinite Loop 無法結束了。

最後,在 setNextRequest 裡帶入 null ,就可以終止整個 CR 流程。

接下來,就看我如何運用上面 7 個小技巧,完成整個 API Script 效果吧!

API Script 步驟詳解

Preset: CSV import

首先,利用 CR import 的功能,模擬 N 位使用者填寫報名表的過程。

email,password,startAt,endAt,courseName
student1@example.com,password,1,5,2021Spring
student2@example.com,password,1,5,2021Spring
student3@example.com,password,1,4,2021Spring
student4@example.com,password,3,5,2021Spring
student5@example.com,password,2,4,2021Spring
student6@example.com,password,1,3,2021Spring

在匯入上面的 CSV file 後,CR 介面會顯示共 6 次的 iteration,每個 iteration 都可引入 emailpasswordstartAtendAtcourseName 這幾個變數。

Step 1. User login

Request 基本資料:

URL: 
[POST] {{local-domain}}/users/login

Request Body (form-data):
- email: {{email}}
- password: {{password}}

再來是 Test Script 的部分:

pm.collectionVariables.set('api-auth-key', null);

pm.test('login success', () => {
pm.response.to.have.status(200);

const json = pm.response.json();
pm.collectionVariables.set('api-auth-key', json.data.token);

pm.collectionVariables.set('week', pm.variables.get('startAt'));

postman.setNextRequest('API 61 Question List of Given Questionnaire Type');
})

在成功登入後,從 response json 中 取得我的 token,並把他儲存到 api-auth-key 這個變數中。

接下來也要開始準備取得問卷的環節,因此取出變數 startAt 並把它設為 week,作為下面的 API 資訊。

Step2. Get Question id

Request 基本資料:

URL: 
[GET] {{local-domain}}/questionnaire/questions/:questionnaire_type/:course_name?week={{week}}

Auth:
API Key, {{api-auth-key}}

Path param:
- questionnaire_type: lecture_feednack
- course_name: {{courseName}}

Query Param:
- week: {{week}}

api-auth-keycourseNameweek 參數都是從之前設定的 variables 中取得。

取得 Test Script 後,我們就可以從 response json 中,取得相對應的題號,儲存為變數:

// TEST SCRIPT
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);

// Collection questionnauirequestion id
const json = pm.response.json();
for (idx in json['data']) {
const idKey = `question_${idx}`;
pm.variables.set(idKey, json['data'][idx]['questionnairequestions_id']);
}

postman.setNextRequest('API 64 Submit Questionnaire-test');
});

從 data 取得 questionnairequestions_id,並將之以 question_n 的形式儲存為變數。

Step3. Submit Answer

Request 基本資料:

Url:
[POST] {{local-domain}}/questionnaire/answers/lecture/:course_name?week={{week}}

Auth:
API Key, {{api-auth-key}}

Path param:
- course_name: {{courseName}}

Query Param:
- week: {{week}}

Request body (JSON):
{
"answers": {{answers}}
}

這邊可以看到在 Request Body 的部分,我使用了 answers 這個變數,至於這個變數的內容,則是由 Pre-Request Script 的部分提供:

// PRE-REQUEST SCRIPT
let i = 0;
let answers = [];
while (true) {
let key = `question_${i}`;
let id = pm.variables.get(key);
if (id == null){
break;
}
answers.push({
'questionnairequestion': id,
'body': pm.variables.replaceIn("{{$randomLoremSentences}}")
})
i++;
}

pm.variables.set('answers', JSON.stringify(answers));

根據在第二部存入的 question_n 的 key-value pair 格式,逐一取出 questionnairequestion 的 id,並用一個 answers array 存入 id 和隨機生成的 body string。

等到取完以後,就把整個 answers 轉為 Json string 存入 Runtime vairable,就可以讓 Request Body 攜帶這些資訊發出 Request 啦!

不過還沒完,等這個 Request 結束以後,還要想辦法回到 step 2 取得下一週問卷的 question id:

// TESTS SCRIPT
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);

// set next run
let week = parseInt(pm.collectionVariables.get('week'));
week++;
if (week > parseInt(pm.variables.get('endAt'))) {
// End of this iteration
postman.setNextRequest(null);
} else {
// Ready to fetch questionnaire question list if for next week
pm.collectionVariables.set('week', week);
postman.setNextRequest('API 61 Question List of Given Questionnaire Type');
}
});

week 變數取出並加一,如果 week 變數還沒有超過 endAt 限制,便將 next request 導向 API 61;如果超過了,就中止整個 CR,開啟下一輪 iteration。

透過這整個 Collection 設定,我成功一鍵完成輸入 6 個使用者對不同週數問卷的填答,省去了一直切換使用者、手動輸入問題 id 的痛苦流程!

在介紹完所有 API 腳本流程中所使用到的功能後,最後再額外介紹兩個我私心推薦的常用功能給大家:

Bonus Tip: Document

文件對於工程師的重要性相信不用我多說,完整的文件說明能夠大幅提升開發效率。

在 Postman 的 Collection / Folde / Request 都有可以撰寫 Document 的頁面,點擊右列的 document tab 以後就可以看得到,Document 支援 md 語法編輯,你可以在上面補充適當的資訊,如這個 Request 的參數組合、 dev 環境可用的帳密等等。

點擊右列 document tab
可以記錄一些關於這個 collection / folder / request 的相關訊息。

Bonus Tip: Global Helper Function

(2024.05 更新:目前這個 feature 可以用更彈性的 Package Library 實作,詳請可看這篇)

Postman 的 script 除了可以 import 模組以外 (這個真的蠻猛的),也可以在 Collection / Folder 層級寫一些自定義的 function,供其中的 Request 使用。

這邊有兩種撰寫這種 Global Helper Function 的方式,在這邊以“隨機產生 YYYY-mm-dd 格式的日期” 為例,你可以在 Collection Pre-Request Script 中撰寫如下:

// 注意這裡不能宣告 const / let / var
helpers = {
randomDateInFuture: function (begin=null, daysInterval=165) {
if (!begin) {
begin = new Date();
}
const future = new Date(begin.getTime() + parseInt(Math.random()*1000*60*60*24*daysInterval));

return future.toISOString().split('T')[0];
}
}

而在 Request 的 Pre-Request Script,你就可以輕鬆調用這個 helper function:

pm.variables.set('acceptanceDate', helpers.randomDateInFuture());

第二個方法則是直接把 helper function 嫁接在 Object.prototype 上面。

// In Collection Pre-request Script
Object.prototype.randomDateInFuture = function () {
// same as above
};

// In Tests Script
var randomDate = _.randomDateInFutre();

兩種方法都可以,就看你比較喜歡哪種風格囉。

比較可惜的是囿於 Postman 的 IDE,即使你在 Helper function 有撰寫關於這個 function 的 docString 細節,也沒辦法像在 vsCode 呼叫的時候看到 function hint,算是美中不足的小缺陷吧~

--

--