POSTMAN 通常是目前在做 API 層級的測試時的首選,不過通常 API 的測試不會只有 End-to-End 的測試就結束了,有些較完整的還會包含負載測試(Load Test),不過若只有用 POSTMAN 就想要完成 Load test 那就辛苦了。
POSTMAN Test Script#
這邊我們就使用 POSTMAN 自帶的 Collection (Postman Echo) 來做實驗,這邊簡單起見就只抓一個 Get method 來做。匯出的檔案大概會長以下這樣:
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  | {
    "info": {
        "_postman_id": "{_postman_id}",
        "name": "{your_collection_name}",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "item": [
        {
            "name": "{your_request_name}",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "id": "e6b1db6b-fac1-4f5c-820e-d0a6806b1a1e",
                        "exec": [
                            "tests[\"Body contains headers\"] = responseBody.has(\"headers\");",
                            "tests[\"Body contains args\"] = responseBody.has(\"args\");",
                            "tests[\"Body contains url\"] = responseBody.has(\"url\");",
                            "",
                            "var responseJSON;",
                            "",
                            "try { responseJSON = JSON.parse(responseBody); }",
                            "catch (e) { }",
                            "",
                            "",
                            "tests[\"Args key contains argument passed as url parameter\"] = 'test' in responseJSON.args"
                        ],
                        "type": "text/javascript"
                    }
                }
            ],
            "request": {
                "method": "GET",
                "header": [],
                "url": {
                    "raw": "https://postman-echo.com/get?test=123",
                    "protocol": "https",
                    "host": [
                        "postman-echo",
                        "com"
                    ],
                    "path": [
                        "get"
                    ],
                    "query": [
                        {
                            "key": "test",
                            "value": "123"
                        }
                    ]
                },
                "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested."
            },
            "response": []
        }
    ],
    "protocolProfileBehavior": {}
}
  | 
這個方式的缺點就是 Postman 匯出的最小單位是 Collection ,所以很可能實際運用時你會需要把本機測試用的 Collection 與壓力測試用的分成兩個 Collection。
What is K6#
過往做負載測試很多都會用 JMeter 來做,JMeter 教學資源很多同時可以自行開發外掛以符合需求,也可以使用 GUI 進行設定,最大的缺點大概就是整體運行偏笨重相依 JVM,而且測試腳本的分享比較不好進行版控(內容比對不容易看出來到底改了什麼),改用 POSTMAN 來進行 API 測試設定快速又好上手,畢竟使用 JMeter 來測試還是比較 heavy 一點。不過使用 POSTMAN 來測試會遇到一個問題,那就是壓力測試 POSTMAN 本身並不支援,為了讓測試的體驗可以更完整,讓壓測可以利用 POSTMAN 完成我們可以借助 K6 這套工具來達成這個效果,讓我們可以把 POSTMAN 所存的 API request 都抓來壓一壓。
Install K6#
官方教學
其實若是 Windows 使用者的話可以直接下載。若是不想處理環境等相關問題的話,K6 本身有提供 Docker 版本可以下載,雖然在執行時會需要啟動 Docker 但是可以確保執行環境的一致性也是個好處。
值得一提的是 K6 雖然是執行 javascript 但是他並不需要 NodeJS,他是用 Golang 寫的,只是附帶一個 javascript 的執行環境。
Convert to K6#
雖然 K6 可以做壓測但他本身並不直接支援 POSTMAN 所匯出的檔案,我們會需要一套轉換工具將 POSTMAN 匯出的 json 檔案轉換成 K6 可以執行的 js 檔案。要做到這個我們可以利用 NodeJS 的一個套件 postman-to-k6 來完成,它能將 POSTMAN 匯出的 json 檔案轉換成 K6 可以理解的形式,包含 PreRequest scripts / Test scripts / variables 等。但是有些行為是不支援的,需要改成 K6 的寫法,無法支援的功能請參考這裡。
讓我們條列一下步驟
Install NodeJS
ref: https://nodejs.org/en/
Install postman-to-k6 module
npm install -g postman-to-k6
Export POSTMAN collection
就是單純的把 POSTMAN 裡要做壓測的 Collection 給 export 出成 json 檔案。
Convert your POSTMAN collection to K6 script
postman-to-k6 postman_collection_export.json -o k6-script.js
轉換完畢應該至少會有兩個檔案,一個 JS 檔就是要 K6 執行的 script,另一個就是他會用到的 library 檔。
就這樣!簡單的四個步驟就完成執行壓測的前置作業了!
產出的檔案大概會類似這樣
 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
32
33
  | // Auto-generated by the Load Impact converter
import "./libs/shim/core.js";
export let options = { maxRedirects: 4 };
const Request = Symbol.for("request");
postman[Symbol.for("initial")]({
  options
});
export default function() {
  postman[Request]({
    name: "GET Request Copy",
    id: "7af25195-60ed-4c1b-b372-1469ea7112c7",
    method: "GET",
    address: "https://postman-echo.com/get?test=123",
    post(response) {
      tests["Body contains headers"] = responseBody.has("headers");
      tests["Body contains args"] = responseBody.has("args");
      tests["Body contains url"] = responseBody.has("url");
      var responseJSON;
      try {
        responseJSON = JSON.parse(responseBody);
      } catch (e) {}
      tests["Args key contains argument passed as url parameter"] =
        "test" in responseJSON.args;
    }
  });
}
  | 
若你跟我一樣抓 POSTMAN Echo 來實驗的話,直接把轉譯過的 script 跑 K6 那肯定會遇到問題,我們在後面一點來解題。
Setup K6 Parameters#
K6 本身提供了很多參數來作為執行時對測試的控制,而最直覺也是最重要的參數莫過於 VUs (virtual users)了,這個就是指明你要模擬多少使用者來打這個 API。不過我個人比較常用的是另外幾個設定,Stages / Thresholds。
Stages 相較於設定 VUs 讓 K6 自己決定人數的變化,使用這個可以控制更多細節,以下是個簡單的範例:
1
2
3
4
5
6
  | stages: [
    { duration: "1m", target: 60},
    { duration: "1m", target: 80},
    { duration: "1m", target: 60},
    { duration: "1m", target: 0}
]
  | 
這邊就是全程跑 4分鐘,然後每一分鐘的 VUs 會到多少,這也是目前我個人比較常用的壓測方式。
而另一個 Thresholds 就是判定怎樣的 request 算是成功,這邊的設定與 POSTMAN 的 test script 所偏向的商業邏輯面比較不同,這邊所設定的閥值通常比較像是效能有關的,可以參考官方的說明:Thresholds。
1
2
3
  | thresholds: {
    http_req_duration: [{ threshold: 'p(95)<100', abortOnFail: false, delayAbortEval: '1m'}],
}
  | 
這個範例就是我指明 request 的全程花費時間的 P 值在信心度 95 的狀況下要有 100ms 就回應我的效能,否則算失敗,且就算失敗也不要停止。而以下就是試跑的結果:

Execute K6 and Export Result#
最簡單又直接的執行指令就是
k6 run k6-script.js
而輸出的話預設就會是在 console 畫面中印出來。也可以把一些 options 當作參數傳入,但我個人偏好把這些 options 寫在檔案裏面。
若你是照搬我這邊的範例的話需要改寫一下 POSTMAN 的 test script 才不會出錯。
首先要在 k6 script 引入
import "./libs/shim/expect.js";
這個套件,我們的測試語法會需要用到它。
而 default function 裡的 post 就改成
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  | // post 這邊傳入的參數其實就是 POSTMAN 打過去的 request 以及相關的數值,涵蓋回應時間等資料
post(pmRequest) {
    pm.test("Body contains headers", function(){
        var jsonData = pm.response.json();
        pm.expect(jsonData).to.have.property("headers");
    });
    pm.test("Body contains args", function(){
        var jsonData = pm.response.json();
        pm.expect(jsonData).to.have.property("args");
    });
    pm.test("Body contains url", function(){
        var jsonData = pm.response.json();
        pm.expect(jsonData).to.have.property("url");
    });
    pm.test("Args key contains argument passed as url parameter", function(){
        var jsonData = pm.response.json();
        pm.expect(jsonData.args).to.have.property("test")
    });
}
  | 
Conclusion#
POSTMAN 在這短短幾年內可以說是席捲了在 API 開發與測試的一套工具,他的簡單易用以及 script 方便納入版控的特性深獲眾多開發者的心,但他目前針對壓測等較為 heavy 的測試的支援是較為不足的,還好有很多好用的工具可以跟他搭配,讓 POSTMAN 與這些測試的相容更為容易,也有越來越多的 QA/QE 在使用這套工具,可以直接把 RD 在開發時的一些 test script 稍加調整後就能併入整合測試/壓力測試中,這也讓開發團隊內部之間的技術可以直接分享。我想未來 POSTMAN 在開發與測試 API 這塊應該會持續佔有領導地位吧~
Reference#