返回文章列表

Karate框架JSONPath與XPath應用

本文探討 Karate 框架中 JSONPath 和 XPath 的應用,解析 XML 和 JSON 資料結構,並提供程式碼範例說明如何使用這些技術進行資料驗證和提取。同時,文章也介紹了 Karate 的 `karate.filter` 函式作為 JSONPath 的替代方案,以及如何使用

Web 開發 測試自動化

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

內容解密:

  1. GraphQL 查詢的多行表示式:查詢被寫成 Karate 的多行表示式,使其更具可讀性。
  2. 使用 text 關鍵字:由於 GraphQL 查詢不符合 JSON 標準,因此使用 text 關鍵字使其被 Karate 接受。
  3. 建構請求主體:使用 Karate 的功能在建構 JSON 物件時使用變數,將包含查詢字串的 userPosts 變數注入請求主體。
  4. 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 鉤子

鉤子是一種特殊的機制,允許接收測試執行中的事件並對其做出反應。在本例中,將使用鉤子輸出有關場景和步驟的資訊。

要使用鉤子,需要執行以下兩個步驟:

  1. 實作一個實作 Karate 鉤子介面(com.intuit.karate.RuntimeHook)的 Java 類別。
  2. 在執行器類別中註冊新的鉤子類別。

實作新的鉤子類別

在我們的範例中,希望新增一些日誌輸出,告訴我們哪個場景已啟動和完成,以及每個步驟的結果是什麼。

首先,建立一個實作 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 的內建功能來將資料扁平化為簡單的字串陣列。

讓我們在場景底部新增幾行程式碼:

  1. 新增一個名為 titles 的空陣列,用於儲存所有貼文標題:
* def titles = []
  1. 定義一個名為 getTitle 的變數,該變數定義了一個函式,接受 postData 引數,提取其 title 屬性,並將其新增到 titles 陣列中:
* def getTitle = function(postData) { karate.appendTo(titles, postData.title) }
  1. 使用 karate.forEach 函式遍歷 response.data.user.posts.data 陣列中的所有條目,並對每個條目套用 getTitle 方法:
* karate.forEach(response.data.user.posts.data, getTitle)
  1. 最後,新增一行 * print titles 以列印結果。

執行場景後,應得到包含所有貼文標題的陣列:

[
  "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "qui est esse",
  "ea molestias quasi exercitationem repellat qui ipsa sit aut",
  ...
]

程式碼解析:

  1. def titles = []:建立一個空陣列 titles 用於儲存貼文標題。
  2. def getTitle = function(postData) { karate.appendTo(titles, postData.title) }:定義一個函式,該函式將貼文資料中的 title 新增到 titles 陣列。
  3. karate.forEach(response.data.user.posts.data, getTitle):遍歷貼文資料並對每個條目套用 getTitle 函式。
  4. * print titles:列印最終的 titles 陣列。

這個過程展示瞭如何使用 Karate 的內建功能和自訂函式來處理和轉換資料。##### 使用 Karate 鉤子的重點:

  1. 鉤子的用途:鉤子允許開發者對測試執行中的特定事件做出反應,例如測試開始、結束或特定步驟的執行結果。
  2. 實作鉤子:需要建立一個 Java 類別,實作 Karate 的 RuntimeHook 介面,並在其中覆寫所需的方法。
  3. 註冊鉤子:在執行器類別中註冊新建立的鉤子類別,以便 Karate 在測試執行期間呼叫相應的方法。

處理資料的重點:

  1. 使用自訂函式:Karate 允許開發者定義自訂函式,以處理和轉換測試資料。例如,可以定義一個函式來提取 JSON 資料中的特定欄位。
  2. 遍歷資料:使用 karate.forEach 等內建函式,可以方便地遍歷 JSON 或陣列資料,並對每個元素執行特定的操作。
  3. 資料轉換:透過結合自訂函式和內建函式,可以將複雜的 JSON 結構扁平化或轉換為其他格式,以滿足測試需求。

在下一章中,我們將進一步探討如何擴充套件 Karate 的功能,包括與外部資料來源整合、處理非原生支援的資料格式,以及最佳化測試效能。這些技術將幫助開發者更靈活地應對多樣化的測試場景,提升測試效率和準確性。