Karate 框架提供 JSONPath 和 XPath 工具,方便處理和驗證 JSON 與 XML 資料。XPath 解析 XML 節點樹,精確定位元素、屬性和文字。JSONPath 則專為 JSON 設計,實作類別似 XPath 的資料存取和過濾功能。文章將以程式碼示例,展示如何在 Karate 中應用這兩種技術,並解析其用法。同時,Karate 的 karate.filter 函式提供另一種過濾 JSON 陣列的方式,搭配自定義函式更具彈性。此外,karate.repeat 命令可結合函式實作資料生成。文章也將探討如何使用 Karate 測試 GraphQL API,包含請求結構、引數設定和回應處理。最後,將介紹 Karate 鉤子(hooks)的應用,以及如何自定義 Java 函式擴充測試功能,並探討如何使測試更簡潔易維護。
深入理解JSONPath與XPath
在測試自動化領域,Karate框架提供了強大的功能來處理JSON和XML資料。其中,JSONPath和XPath是兩個非常有用的工具,能夠幫助我們更靈活地存取和驗證資料。本文將詳細介紹這兩個概念及其在Karate中的應用。
XPath詳解
XPath是一種用於解析XML結構的語言,它將XML檔案視為由元素、屬性和文字組成的節點樹。透過XPath,我們可以輕鬆地找到XML中的特定部分。
XPath例項
假設我們有以下的XML結構:
<magicians>
<magician id="1">
<name>Harry Houdini</name>
<birthyear>1874</birthyear>
<specialty>escapology</specialty>
<specialty>card tricks</specialty>
</magician>
<magician id="2">
<name>David Copperfield</name>
<birthyear>1956</birthyear>
<specialty>illusions</specialty>
</magician>
</magicians>
我們可以使用XPath表示式來驗證資料。例如:
* match data count(/magicians/magician) == 2
* match data //magician[@id='2']/name == 'David Copperfield'
* def name = get data //magician[@id='2']/name
* print "Magician with id 2 is", name
* match data //magician[name = 'Harry Houdini']/specialty == ['escapology', 'card tricks']
程式碼解析
* match data count(/magicians/magician) == 2
此行程式碼計算<magicians>標籤下<magician>標籤的數量,並斷言其等於2。
程式碼解析
* def name = get data //magician[@id='2']/name
此行程式碼使用XPath表示式//magician[@id='2']/name來取得id為2的魔術師的名字,並將結果儲存在變數name中。
JSONPath詳解
JSONPath是一種用於存取和過濾JSON資料的表示式語言。它與XPath類別似,但專門為JSON設計。
JSONPath例項
假設我們有以下的JSON結構:
{
"magicians": [
{
"id": 1,
"name": "Harry Houdini",
"birthyear": 1874,
"specialty": ["escapology", "card tricks"]
},
{
"id": 2,
"name": "David Copperfield",
"birthyear": 1956,
"specialty": ["illusions"]
}
]
}
我們可以使用JSONPath表示式來驗證資料。例如:
* match data.magicians == '#[2]'
* def name = get[0] data.magicians[?(@.id==2)].name
* match name == 'David Copperfield'
* def specialties = get[0] data.magicians[?(@.name == 'Harry Houdini')].specialty
* match specialties == ['escapology', 'card tricks']
程式碼解析
* def name = get[0] data.magicians[?(@.id==2)].name
此行程式碼使用JSONPath表示式data.magicians[?(@.id==2)].name來取得id為2的魔術師的名字。由於結果是一個陣列,因此使用get[0]來取得第一個元素。
使用karate.filter作為替代方案
Karate提供了karate.filter函式作為JSONPath的替代方案,用於過濾陣列。
karate.filter例項
* def illusionistFilter = function(x){ return x.specialty.contains("illusions"); }
* def illusionists = karate.filter(data.magicians, illusionistFilter)
* print illusionists
程式碼解析
* def illusionistFilter = function(x){ return x.specialty.contains("illusions"); }
此行程式碼定義了一個名為illusionistFilter的函式,用於過濾具有"illusions"特技的魔術師。
程式碼解析
* def illusionists = karate.filter(data.magicians, illusionistFilter)
此行程式碼使用karate.filter函式和illusionistFilter來過濾data.magicians陣列,取得具有"illusions"特技的魔術師。
更進階的 Karate 功能
karate.repeat 的應用
Karate 的 karate.filter 機制允許使用預先定義的函式作為引數,同樣可用於資料生成。karate.repeat 命令接受一個計數和一個函式作為引數,可用於迴圈。它將按照指定的次數執行該函式,並將結果收集到陣列中。
測試 GraphQL API
GraphQL 是由 Facebook 開發的 API 查詢語言,提供了比傳統 REST API 更高效、更靈活的替代方案。客戶端可以透過單一請求指定所需的資料,伺服器將以可預測和有組織的方式傳回請求的資料。這減少了不必要的資料傳輸,提高了效能。
瞭解 GraphQL 請求
GraphQL 請求是一種特殊的 JSON 衍生格式,透過 POST 請求傳送。儘管看起來與 JSON 相似,但它並不是有效的 JSON。請求主體中始終包含一個 query 物件,用於定義應傳回的資料部分。此外,還可以在請求的元素中指定引數(括號內),這在純 JSON 中是無效的。
使用 Karate 測試 GraphQL
我們將使用 GraphQLZero(https://graphqlzero.almansi.me),一個公開的假 API,來示範如何使用 Karate 測試 GraphQL 端點。
Scenario: 查詢使用者文章
* url "https://graphqlzero.almansi.me/api"
* text userPosts =
"""
{
user(id: 1) {
name,
posts {
data {
title
}
}
}
}
"""
* request { query: '#(userPosts)' }
When method post
* print response.data.user.name
內容解密:
- GraphQL 查詢的多行表示式:查詢被寫成 Karate 的多行表示式,使其更具可讀性。
- 使用
text關鍵字:由於 GraphQL 查詢不符合 JSON 標準,因此使用text關鍵字使其被 Karate 接受。 - 建構請求主體:使用 Karate 的功能在建構 JSON 物件時使用變數,將包含查詢字串的
userPosts變數注入請求主體。 response處理:回應包含在data元素內的合法 JSON,因此無需進一步轉換。
探索 GraphQLZero API
GraphQLZero 提供了一個線上 playground,可以在其中編寫和執行請求,並提供了完整的檔案說明可用的查詢和回應。
使用 Postman 測試 GraphQL
Postman 允許指定 GraphQL 作為請求主體的格式,並提供語法錯誤高亮顯示,方便在傳送請求前檢查錯誤。
重點整理
- GraphQL 提供比傳統 REST API 更高效的資料檢索方式。
- Karate 可以透過
text關鍵字處理 GraphQL 查詢,並透過變數注入建構請求主體。 - GraphQLZero 和 Postman 是測試和探索 GraphQL API 的有用工具。
自訂與最佳化 Karate 測試
Karate 已經提供了許多內建功能和特性,但有時仍需要新增自訂功能以滿足特定需求,例如處理多種資料來源或不支援的資料格式。此外,隨著測試案例的增加,測試套件可能會變得龐大,因此必須思考如何保持測試簡潔、可維護且易於理解。
本章將探討以下主題:
- 使用 Karate 鉤子(hooks)
- 定義和呼叫 Java 函式
- 使用 Karate 作為 mock 伺服器
- 使測試更加簡潔
技術需求
您需要以下內容:
- 在第 2 章中完成的系統和 IDE 設定
- Postman 用於探索 mock 範例
- MySQL 資料函式庫(如果沒有本地資料函式庫,將說明如何設定免費的線上例項)
使用 Karate 鉤子
鉤子是一種特殊的機制,允許接收測試執行中的事件並對其做出反應。在本例中,將使用鉤子輸出有關場景和步驟的資訊。
要使用鉤子,需要執行以下兩個步驟:
- 實作一個實作 Karate 鉤子介面(
com.intuit.karate.RuntimeHook)的 Java 類別。 - 在執行器類別中註冊新的鉤子類別。
實作新的鉤子類別
在我們的範例中,希望新增一些日誌輸出,告訴我們哪個場景已啟動和完成,以及每個步驟的結果是什麼。
首先,建立一個實作 Karate RuntimeHook 介面的類別:
package hooks;
import com.intuit.karate.RuntimeHook;
public class KarateHooks implements RuntimeHook {
// 在此處實作鉤子方法
}
這個類別稱為 KarateHooks,位於 hooks 套件中,與 examples 套件同級,直接位於 src/test/java 下。
可用的鉤子方法
圖 7.2 中顯示了 Karate 1.2.0 中可用的方法。如果使用 VS Code,可以按 Ctrl + 空格鍵 顯示此列表並選擇要覆寫的方法。
大多數這些方法都與測試場景相關。其中一個例外是 beforeHttpCall() 和 afterHttpCall(),可用於對 Web 服務呼叫做出反應,甚至在執行前攔截和更改它們。
在我們的範例中,將使用其他一些方法,因為它們更清楚地展示了整體概念。
將資料收集到新陣列中
在某些情況下,我們可能只對特定資料感興趣,例如貼文的標題。這時,可以使用自訂函式和 Karate 的內建功能來將資料扁平化為簡單的字串陣列。
讓我們在場景底部新增幾行程式碼:
- 新增一個名為
titles的空陣列,用於儲存所有貼文標題:
* def titles = []
- 定義一個名為
getTitle的變數,該變數定義了一個函式,接受postData引數,提取其title屬性,並將其新增到titles陣列中:
* def getTitle = function(postData) { karate.appendTo(titles, postData.title) }
- 使用
karate.forEach函式遍歷response.data.user.posts.data陣列中的所有條目,並對每個條目套用getTitle方法:
* karate.forEach(response.data.user.posts.data, getTitle)
- 最後,新增一行
* print titles以列印結果。
執行場景後,應得到包含所有貼文標題的陣列:
[
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"qui est esse",
"ea molestias quasi exercitationem repellat qui ipsa sit aut",
...
]
程式碼解析:
def titles = []:建立一個空陣列titles用於儲存貼文標題。def getTitle = function(postData) { karate.appendTo(titles, postData.title) }:定義一個函式,該函式將貼文資料中的title新增到titles陣列。karate.forEach(response.data.user.posts.data, getTitle):遍歷貼文資料並對每個條目套用getTitle函式。* print titles:列印最終的titles陣列。
這個過程展示瞭如何使用 Karate 的內建功能和自訂函式來處理和轉換資料。##### 使用 Karate 鉤子的重點:
- 鉤子的用途:鉤子允許開發者對測試執行中的特定事件做出反應,例如測試開始、結束或特定步驟的執行結果。
- 實作鉤子:需要建立一個 Java 類別,實作 Karate 的
RuntimeHook介面,並在其中覆寫所需的方法。 - 註冊鉤子:在執行器類別中註冊新建立的鉤子類別,以便 Karate 在測試執行期間呼叫相應的方法。
處理資料的重點:
- 使用自訂函式:Karate 允許開發者定義自訂函式,以處理和轉換測試資料。例如,可以定義一個函式來提取 JSON 資料中的特定欄位。
- 遍歷資料:使用
karate.forEach等內建函式,可以方便地遍歷 JSON 或陣列資料,並對每個元素執行特定的操作。 - 資料轉換:透過結合自訂函式和內建函式,可以將複雜的 JSON 結構扁平化或轉換為其他格式,以滿足測試需求。
在下一章中,我們將進一步探討如何擴充套件 Karate 的功能,包括與外部資料來源整合、處理非原生支援的資料格式,以及最佳化測試效能。這些技術將幫助開發者更靈活地應對多樣化的測試場景,提升測試效率和準確性。