Karate 是一款根據 Cucumber 的 API 測試框架,它結合了 BDD 的易讀性和 API 測試的靈活性。Karate DSL 簡潔易懂,即使不熟悉程式語言的測試人員也能快速上手。它支援 JSON、XML、字串等多種資料格式的驗證,並提供豐富的內建匹配器和斷言,方便開發者進行精確的 API 測試。此外,Karate 還支援資料驅動測試和在 IDE 中的斷點除錯,提升測試效率。

使用 Karate 編寫基礎測試

驗證回應型別

在進行 API 測試時,驗證回應的型別是非常重要的一步。Karate 提供了 responseType 關鍵字來檢查回應的型別。例如,若要驗證回應是否為 JSON 格式,可以使用以下程式碼:

Scenario: 檢查特定使用者的文章
Given url 'https://jsonplaceholder.typicode.com/posts'
When method get
Then status 200
And match responseType == 'json'

內容解密:

  • responseType 可以被設定為 jsonstringxml,用於驗證回應的型別。
  • 使用雙等號 == 進行值比較。
  • And 關鍵字用於新增額外的檢查步驟,相當於另一個 Then 步驟。

比對值

最簡單的比對案例是檢查某個元素是否具有特定的值。例如,驗證第一個傳回的文章中 userId 是否為 1:

And match response[0].userId == 1

內容解密:

  • 使用 == 進行值的比較。
  • 若比對失敗,Karate 將提供詳細的錯誤訊息,指出期望值與實際值的差異。
  • 也可以使用 != 來檢查值是否不相等。

檢查回應元素是否存在

在進行值比對之前,應該先檢查預期的鍵是否存在於回應中。可以使用 match contains 來達成此目的:

And match response[0] contains { userId: 1 }

內容解密:

  • match contains 用於檢查回應中是否包含特定的鍵值對。
  • 可以使用 !contains 來檢查某個元素是否不存在於回應中。
  • 可以同時比對多個鍵值對。

使用模糊比對器

模糊比對器用於當你不確定值的確切內容,但知道其結構或資料型別時。可以使用特殊的標記(以 # 符號開頭)來檢查不同的條件。例如,檢查某個鍵是否存在:

And match response[0] == { id: '#present', userId: '#present', title: '#present', body: '#present' }

內容解密:

  • #present 標記用於檢查某個鍵是否存在。
  • #notpresent 標記用於檢查某個鍵是否不存在。
  • 其他可用的標記包括 #null#notnull#array#object#boolean#number#string,用於檢查資料型別。

使用斷言和匹配器驗證回應內容

在進行API測試時,驗證回應內容的正確性是非常重要的。Karate提供了一系列強大的工具來幫助我們完成這項任務。

驗證JSON資料

當我們收到JSON格式的回應時,可以使用Karate的匹配器來驗證其內容。例如:

And match response[0] == { id: '#number', userId: '#number', title: '#string', body: '#string' }

這行程式碼檢查了回應的第一個元素是否符合預期的資料型別。

內容解密:

  1. response[0]:存取回應的第一個元素。
  2. id: '#number':檢查id欄位是否為數字。
  3. userId: '#number':檢查userId欄位是否為數字。
  4. title: '#string':檢查title欄位是否為字串。
  5. body: '#string':檢查body欄位是否為字串。

如果我們將其中一個欄位的資料型別改為錯誤的型別(例如,id: '#string'),Karate會提供詳細的錯誤訊息,指出哪個欄位出了問題。

選用值

除了檢查欄位是否存在或為特定值外,Karate還允許我們檢查選用值。我們可以在匹配器中使用##來表示一個欄位是選用的。例如:

{ 'userId': '##number', 'id': '##string' }

這表示userIdid欄位是選用的,如果它們存在,則必須符合指定的資料型別。

內容解密:

  1. ##number:表示該欄位是選用的數字。
  2. 如果欄位存在,則檢查其資料型別;如果不存在或為空,則忽略檢查。

驗證陣列元素

要驗證陣列中的所有元素,可以使用match each語法。例如:

And match each response == { id: '#number', userId: '#number', title: '#string', body: '#string' }

這行程式碼會檢查回應陣列中的每個元素是否符合預期的資料型別。

內容解密:

  1. match each response:對回應陣列中的每個元素進行匹配檢查。
  2. 後面的JSON物件定義了預期的資料型別。

我們還可以結合contains來驗證陣列元素中特定欄位的值。例如:

And match each response contains { userId: 1 }

這行程式碼檢查每個陣列元素是否包含userId為1的欄位。

內容解密:

  1. match each response contains:檢查每個陣列元素是否包含指定的欄位和值。
  2. { userId: 1 }:定義了預期的欄位和值。

驗證JSON結構

Karate提供了簡便的方法來驗證JSON結構,不需要使用複雜的JSON Schema。

驗證陣列

可以使用#[]標記來驗證回應是否為陣列。例如:

And match response == '#[]'

這行程式碼檢查回應是否為陣列。

內容解密:

  1. #[]:表示預期回應為陣列。

我們還可以指定陣列的大小:

And match response == '#[10]'

這表示預期回應是包含10個元素的陣列。

內容解密:

  1. #[10]:指定陣列的大小為10。

進一步地,我們可以結合多個檢查:

And match response == '#[] #object? _.userId == 1'

這行程式碼結合了多個檢查:

  1. 回應是陣列嗎?
  2. 每個陣列元素都有userId屬性且值為1嗎?

內容解密:

  1. #[]:檢查回應是陣列。
  2. #object:檢查陣列元素是物件。
  3. ? _.userId == 1:檢查每個物件的userId屬性是否為1。

這樣的語法使得我們能夠在單一行中執行多個檢查,大大提高了測試的效率和可讀性。

使用Karate進行基本測試

傳送帶有請求負載的請求

到目前為止,我們只處理了回應資料。但如果是傳送包含請求負載的請求,如POST、PUT和PATCH,該怎麼辦?讓我們來看看JSONPlaceholder API的另一個部分,它允許我們做到這一點。

非永續性模擬API

需要注意的是,JSONPlaceholder API是一個非永續性的API。這意味著無論我們傳送多少次建立或修改資料的請求,其底層資料都不會被改變。它只是模擬了一個真實的API傳回了相應的回應。 這使得它非常適合用於測試。

根據使用者,我們可以看到/posts端點支援POST操作,以建立一個新的文章。 根據檔案,傳送到端點的請求體應該如下所示:

{
  "title": "foo",
  "body": "bar",
  "userId": 1
}

傳回的值應該是建立的文章,包括其ID:

{
  "id": 101,
  "title": "foo",
  "body": "bar",
  "userId": 1
}

由於請求體和回應都很簡單,這是一個很好的例子來說明POST請求。

傳送帶有請求負載的請求

讓我們在posts.feature檔案中建立一個新的場景,名為「Creating a new post」,內容如下:

Scenario: Creating a new post
  Given url 'https://jsonplaceholder.typicode.com'
  And path 'posts'
  And request { userId: 10, title: 'Hello', body: 'World' }
  When method post
  Then status 201
  And match responseType == 'json'
  And match response == { id: #number, userId: 10, title: 'Hello', body: 'World' }

使用變數和資料表格

Karate允許將值儲存到變數中,以便在一個測試中多次使用相同的JSON體或值,而不必在多個地方更改它們,如果我們想用不同的值進行測試。資料表格則允許更可讀地定義JSON資料。

使用變數

變數是Karate測試中非常強大的工具。它們可以儲存幾乎任何值型別,因此非常靈活。 要宣告一個簡單的變數,可以使用def關鍵字,表示定義。在下面的例子中,我們將字串「Benjamin」儲存在myName變數中,並在print陳述式中使用它:

Scenario: Declaring a variable
  * def myName = 'Benjamin'
  * print 'Hello from', myName

執行時,這將輸出Hello from Benjamin,顯示myName的內容符合預期。

將請求負載儲存在變數中

讓我們首先將初始請求負載儲存在一個名為payload的新變數中,並使用它來傳送POST請求:

Scenario: Creating a new post with variable
  Given url 'https://jsonplaceholder.typicode.com'
  And path 'posts'
  * def payload = { userId: 10, title: 'Hello', body: 'World' }
  And request payload
  When method post
  Then status 201
  And match responseType == 'json'
  And match response contains payload

程式碼解析:

  1. * def payload = { userId: 10, title: 'Hello', body: 'World' }:定義了一個名為payload的變數,用於儲存請求負載。
  2. And request payload:使用定義的payload變數作為請求負載傳送POST請求。
  3. And match response contains payload:檢查回應是否包含與payload相同的內容。

重構檢查邏輯

由於我們的API傳回的回應包含一個額外的id欄位,我們需要調整檢查邏輯以匹配回應中的部分內容。

And match response contains { userId: 10, title: 'Hello', body: 'World' }

或者使用之前定義的payload變數:

And match response contains payload

這樣,我們可以確保回應中包含了我們傳送的請求負載內容。

使用 Karate 編寫基本 API 測試

在前一章節中,我們遇到了回應與請求不完全匹配的問題,因為回應中多了一個 id 變數。本章節將介紹幾種解決方案,並探討如何使用 Karate 編寫基本的 API 測試。

簡化回應匹配

我們的 payload 變數持有一個 JSON 物件。因此,我們可以在進行檢查之前,將額外的 id 鍵新增到我們的 JSON 變數中。由於我們知道它應該是一個數字,因此我們也可以使用特殊的 #number 標記。

* payload.id = "#number"
* print payload
And match response == payload

執行測試後,應該會透過並列印出新的 payload 變數:

{
  "userId": 10,
  "title": "Hello",
  "text": "World",
  "id": "#number"
}

內容解密:

  1. payload.id = "#number":將 id 屬性新增到 payload JSON 物件中,並使用 #number 標記表示它應該是一個數字。
  2. print payload:列印修改後的 payload 變數,以驗證其內容。
  3. And match response == payload:將回應與修改後的 payload 進行匹配,確保它們相同。

使用資料表格

當處理複雜的 JSON 陣列時,使用資料表格可以使測試更具可讀性。

* table numbersAndWords
| number | word |
| 5 | 'five' |
| 10 | 'ten' |
* print numbersAndWords

輸出結果:

[
  {
    "number": 5,
    "word": "five"
  },
  {
    "number": 10,
    "word": "ten"
  }
]

內容解密:

  1. table numbersAndWords:定義一個名為 numbersAndWords 的資料表格。
  2. 資料表格的第一行是標頭,定義了 JSON 物件的鍵。
  3. 後續行被轉換為 JSON 陣列元素,每個元素的鍵來自標頭,值來自對應的行。

使用資料表格建立測試案例

Scenario: 使用資料表格建立新帖子
Given url 'https://jsonplaceholder.typicode.com'
And path 'posts'
* table payload
| userId | title | text |
| 10 | 'Hello' | 'World' |
* def payload = payload[0]
And request payload
When method post
Then status 201
And match responseType == 'json'
* payload.id = "#number"
And match response == payload

內容解密:

  1. table payload:定義一個名為 payload 的資料表格,用於建立測試資料。
  2. def payload = payload[0]:將 payload 變數設定為其第一個元素,以便後續使用。
  3. 後續步驟與前面的範例相同,使用修改後的 payload 傳送請求並驗證回應。

使用 set 建立測試資料

Scenario: 使用 set 建立新帖子
Given url 'https://jsonplaceholder.typicode.com'
And path 'posts'
* set payload
| path | 0 |
| userId | 10 |
| title | 'Hello' |
| text | 'World' |
* def payload = payload[0]
And request payload
When method post
Then status 201
And match responseType == 'json'
* payload.id = "#number"
And match response == payload

內容解密:

  1. set payload:使用 set 語法建立測試資料,鍵在左列,值在右列。
  2. 第一列的標頭必須包含 path,用於指定鍵的路徑。
  3. 後續步驟與前面的範例相同,使用建立的 payload 傳送請求並驗證回應。

在IDE中執行和除錯Karate測試

在本章中,我們將探討如何透過IDE執行和除錯Karate測試。首先,我們將瞭解如何使用Karate CLI執行測試,然後我們將重點放在如何透過IDE進行除錯。

使用Karate CLI執行測試

在之前的章節中,我們使用了Karate CLI來執行測試。這對於簡單的測試觸發是可以的,但是我們希望對測試有更多的控制權。因此,我們不會在這裡使用它。

更多關於Karate CLI功能的資訊可以在https://github.com/karatelabs/karate/wiki/Debug-Server#karate-cli找到。接下來,我們將看看如何直接從IDE除錯測試。

透過CodeLens和Karate standalone除錯

您可能記得,在第2章“設定您的Karate專案”中,我們討論了Karate standalone的基本功能。在本文中,我們將再次使用它來舒適地除錯我們的測試。

如果我們現在嘗試使用CodeLens中的“Karate:除錯”選項,它將無法工作。這是因為缺少兩個關鍵部分:Karate standalone的路徑和啟動組態。

組態Karate standalone

在此步驟中,我們將組態Karate standalone的路徑,以便用於除錯。

  1. 透過點選右上角的Karate按鈕並選擇“Open Settings”(或透過“File | Preferences | Settings”選單)開啟設定。
  2. 尋找“Karate Runner > Karate Jar:Command Line Args”條目,並將karate.jar替換為Karate standalone目錄中karate.jar的完整路徑。
圖4.4 – 組態Karate JAR路徑

如截圖所示,我們的路徑是C:\Users\bbischoff\Desktop\karate-1.2.1.RC1\karate.jar。您的路徑當然會不同,並且可能具有不同的版本號。

重要的是保持命令的其他部分不變。

建立啟動組態

要建立除錯啟動組態,我們可以點選任何測試場景上方的CodeLens中的“Karate:除錯”連結。VS Code將提示我們選擇要使用的偵錯程式。

圖4.5 – 選擇偵錯程式

在這裡,我們選擇了“Karate(debug)”。VS Code現在將建立並開啟位於專案.vscode目錄下的launch.json檔案。該檔案包含一個或多個用於除錯的組態。

圖4.6 – Karate除錯啟動組態

您可以看到,在檔案的組態部分,有一個名為“Karate(debug):Standalone”的組態,表明將使用Karate standalone來執行它。

現在,我們應該能夠透過再次點選“Karate:除錯”CodeLens選項來成功使用它。

使用除錯伺服器

尤其是在測試開發過程中,瞭解測試在特定步驟中的當前狀態、詳細的請求和回應、以及如果選擇其他值會發生什麼是非常有趣的。所有這些都可以使用我們現在擁有的除錯伺服器設定來實作。

設定斷點

您可以在要除錯的Karate場景中設定斷點。這意味著當到達具有斷點的行時,執行將暫停。

要設定斷點,請點選應該發生斷點的行的左側。在這裡,我們在第10行設定了一個斷點,即Then status 200

圖4.8 – 設定斷點

設定的斷點也將出現在VS Code的左下角,以及檔名、路徑和行號。

圖4.9 – 斷點列表

也可以設定多個斷點,以檢視多個步驟中發生的情況。

讓我們看看現在除錯測試時會發生什麼。

探索當前狀態

點選具有活動斷點的“Karate:除錯”將在到達該行時停止執行。同時,它會被突出顯示以直觀地顯示這一點。

圖4.10 – 到達斷點時突出顯示的行

在VS Code的左側,這也反映在“CALL STACK”部分。

圖4.11 – VS Code呼叫堆積疊

最重要的部分是“VARIABLES”。在這裡,我們可以看到當前步驟中所有相關的變數。

圖4.12 – VS Code斷點變數

在這種情況下,我們看到response JSONresponseHeadersresponseStatus,甚至可以展開它們以檢視完整內容。這使得除錯非常簡單直接。

前後步進

當斷點被觸發且執行停止時,在VS Code的頂部會出現一個新的除錯工具欄。

圖4.13 – 除錯工具欄
// 示例程式碼
public class DebugExample {
    public static void main(String[] args) {
        // 在這裡設定斷點
        int result = add(2, 3);
        System.out.println(result);
    }

    public static int add(int a, int b) {
        return a + b;
    }
}

內容解密:

此Java程式碼演示了一個簡單的加法運算。首先,在main方法中呼叫add方法,將2和3相加,並將結果儲存在result變數中。然後,列印預出結果。在add方法中,簡單地傳回兩個輸入整數的和。可以在int result = add(2, 3);行設定斷點,以除錯程式並檢視變數的值。