返回文章列表

Karate進階測試組態與執行選項

本文探討 Karate 測試框架的進階功能,包含根據條件執行或中止測試、呼叫系統命令、設定系統屬性、請求重試機制、嵌入表示式在 JSON 與 XML 的應用,以及如何讀取和處理外部 JSON、XML、CSV、YAML 和 JavaScript 檔案,有效提升 API 測試效率與程式碼彈性。

測試

Karate 除了基本 API 測試功能外,也提供許多進階技巧,讓開發者能更靈活地控制測試流程。本文將介紹條件式測試執行、系統屬性設定、請求重試機制,以及嵌入表示式和外部檔案處理等進階功能。透過這些技巧,可以根據不同環境或需求調整測試案例,提升測試效率和程式碼可維護性。此外,Karate 也支援讀取外部檔案,包含 JSON、XML、CSV、YAML 等格式,讓測試資料管理更方便。嵌入表示式功能則讓測試案例更具彈性,可以根據變數或函式動態生成測試資料。

使用不同的組態與執行選項

接下來,我們將探討如何在Karate中根據不同的條件來執行測試,以及如何使用自訂的系統屬性。

根據條件中止測試

在某些情況下,我們需要根據特定的條件來決定是否中止測試。Karate提供了karate.abort()方法來實作這一功能。下面是一個例子:

Scenario: 只在Windows上執行

  • def isWindows = karate.os.type == “windows”
  • if (!isWindows) karate.abort()
  • print “我正在Windows上執行!”

在這個例子中,如果isWindows變數為false,則karate.abort()會中止測試,不會執行後續的步驟。

根據條件失敗測試

除了中止測試外,我們還可以使用karate.fail()方法來使測試失敗。下面是一個例子:

Scenario: 只在Windows上執行,否則失敗

  • def isWindows = karate.os.type == “windows”
  • if (!isWindows) karate.fail(“這不是Windows!”)
  • print “我正在Windows上執行!”

在這個例子中,如果isWindows變數為false,則karate.fail()會使測試失敗,並輸出自訂的錯誤訊息。

呼叫作業系統命令

Karate提供了karate.exec()方法來呼叫作業系統命令。下面是一個例子:

Scenario: 取得Java資訊

  • def javaInfo = karate.exec(“java –version”)
  • print javaInfo

這個例子使用karate.exec()方法來呼叫java --version命令,並將輸出結果儲存在javaInfo變數中。

內容解密:

  • karate.exec("java --version"):使用karate.exec()方法呼叫java --version命令,取得Java版本資訊。
  • def javaInfo:將命令的輸出結果儲存在javaInfo變數中。
  • print javaInfo:輸出Java版本資訊。

非同步執行

如果需要呼叫不會阻塞Karate測試執行的系統命令,可以使用karate.fork()方法。

傳遞自訂系統屬性

Karate允許我們使用自訂的系統屬性。下面是一個例子:

function fn() {
  var config = {
    name: karate.properties["name"]
  }
  return config;
}

在這個例子中,我們使用karate.properties陣列來存取自訂的系統屬性。

Scenario: 使用系統屬性

  • print “嗨,我的名字是”, name

我們可以使用@Karate.Test註解來設定自訂的系統屬性:

@Karate.Test
Karate testProperty() {
  return Karate.run("properties")
    .systemProperty("name", "Jane")
    .relativeTo(getClass());
}

或者使用Java的System.setProperty()方法:

@Test
void testParallel() {
  System.setProperty("name", "Richard");
  Results results = Runner.path("classpath:examples")
    .parallel(5);
  assertEquals(0, results.getFailCount(),
    results.getErrorMessages());
}

請求重試

Karate提供了內建的請求重試機制,使用retry until關鍵字。下面是一個例子:

Scenario: 根據狀態碼重試 Given url ‘https://jsonplaceholder.typicode.com/todos/1' And retry until responseStatus == 200 When method get

在這個例子中,Karate會重試請求,直到狀態碼為200。

內容解密:

  • retry until responseStatus == 200:使用retry until關鍵字來重試請求,直到狀態碼為200。
  • responseStatus:Karate內建的變數,用於存取HTTP回應的狀態碼。

設定重試行為

我們可以在karate-config.js中設定重試行為:

karate.configure('retry', { count:2, interval:1000 });

在這個例子中,我們設定重試次數為2,間隔為1秒。

使用不同的組態和執行選項

在測試程式碼中組態重試

我們可以在功能檔案中直接組態重試,以自定義特定測試案例的行為。這可以透過我們在前幾章中已經瞭解的 configure 關鍵字來完成:

Scenario: 根據自定義條件重試
Given url 'https://jsonplaceholder.typicode.com/todos/1'
* configure retry = { count: 2, interval: 5000 }
And retry until response.userId == 1 && response.name == "test"
When method get

內容解密:

  • configure retry 用於設定重試次數和間隔。
  • count: 2 表示重試次數為2次。
  • interval: 5000 表示每次重試之間的間隔為5000毫秒(5秒)。
  • retry until 用於指定重試條件,直到條件滿足為止。

使用進階標籤

使用標籤執行特定環境的場景

在某些情況下,我們可能希望某些場景只在特定的環境中執行。Karate 提供了 @env@envnot 標籤來實作這一需求。

Feature: 使用標籤
@env=dev
Scenario: 僅開發環境
* print "DEV ONLY"

@env=dev,prod
Scenario: 開發和生產環境
* print "DEV and PROD"

@envnot=dev
Scenario: 非開發環境
* print "Not in DEV"

內容解密:

  • @env=dev 表示該場景僅在開發環境中執行。
  • @env=dev,prod 表示該場景在開發和生產環境中執行。
  • @envnot=dev 表示該場景不在開發環境中執行。

使用值標籤

值標籤允許我們對標籤賦予特定的值,從而實作更靈活的測試控制。

Feature: 值標籤
@car=audi
Scenario: 值標籤
* print karate.tagValues

@car=mercedes,tesla
Scenario: 值標籤
* print karate.tagValues

內容解密:

  • @car=audi@car=mercedes,tesla 是值標籤的例子。
  • karate.tagValues 用於儲存場景標籤的JSON結構,可以在場景或自定義方法中使用。

定義和使用表示式與 def

定義內聯方法

使用 def 關鍵字可以定義輔助函式,以避免重複相同的計算或字串操作。

Scenario: 英里和公里轉換
* def kmToMiles = function(km) { return km / 1.6 }
* def milesToKm = function(miles) { return miles * 1.6 }
* assert kmToMiles(16) == 10
* def miles = kmToMiles(90)
* match miles == 56.25
* match milesToKm(miles) == 90

內容解密:

  • kmToMilesmilesToKm 是兩個函式,分別用於公里到英里和英里到公里的轉換。
  • 使用 def 定義函式,並透過呼叫函式進行斷言或變數指定。

使用嵌入式表示式與 JSON

Karate 支援範本方法,可以動態設定 JSON 或 XML 檔案中的佔位符。

Scenario: 使用 JSON 範本
* def title = 'Testing APIs'
* def pages = 250
* def chapters = ['Core concepts', 'Setup']
* def titleBackwards = function(title) { return title.split("").reverse().join(""); }
* def format = null
* def book =
"""
{
  title: '#(title)',
  pages: #(pages),
  chapters: #(chapters),
  totalChapters: #(chapters.length),
  totalChaptersPlusIntro: #(chapters.length + 1),
  titleBackwards: '#(titleBackwards(title))',
  format: '##(format)'
}
"""
* print book

內容解密:

  • #() 表示式語法用於範本替換,可以使用變數和函式來動態生成 JSON 物件。
  • 在字串表示式中需要使用引號包圍,以確保生成的 JSON 是有效的。

使用Karate進行進階測試:嵌入表示式與外部檔案處理

Karate提供了一系列強大的功能,能夠簡化API測試的流程並提高測試的可讀性。其中,嵌入表示式(Embedded Expressions)與外部檔案處理是兩個非常實用的功能。

嵌入表示式的使用

嵌入表示式允許在JSON或XML範本中直接嵌入變數或運算式,從而實作動態內容的生成。以下是一個JSON範例,展示瞭如何使用嵌入表示式:

{
  "title": "#(title)",
  "pages": #(pages),
  "chapters": [
    "Core concepts",
    "Setup"
  ],
  "totalChapters": #(chapters.length),
  "totalChaptersPlusIntro": #(chapters.length + 1),
  "titleBackwards": '#(titleBackwards(title))'
}

內容解密:

  • "title": "#(title)":將title變數的值嵌入到JSON中。
  • "pages": #(pages):將pages變數的值嵌入到JSON中。
  • "totalChapters": #(chapters.length):計算chapters陣列的長度並嵌入結果。
  • "totalChaptersPlusIntro": #(chapters.length + 1):進行簡單的數學運算,將結果嵌入。
  • "titleBackwards": '#(titleBackwards(title))':呼叫自定義函式titleBackwards並將結果嵌入。

移除JSON元素

Karate提供了remove關鍵字,用於從已有的JSON中移除元素。例如,* remove book.title將移除book物件中的title屬性。

使用嵌入表示式與XML

嵌入表示式同樣適用於XML範本。以下是一個範例:

<book>
  <title>#(title)</title>
  <pages>#(pages)</pages>
</book>

內容解密:

  • <title>#(title)</title>:將title變數的值嵌入到XML中。
  • <pages>#(pages)</pages>:將pages變數的值嵌入到XML中。

處理外部檔案

Karate支援從外部檔案讀取資料,包括JSON、XML、CSV等格式。以下是一些範例:

JSON檔案處理

Scenario: Read JSON
* def json = read('../json-example.json')
* assert json.name == "Karate"
* assert json.inception == 2017

內容解密:

  • read('../json-example.json'):讀取JSON檔案並將內容存入json變數。
  • assert json.name == "Karate":驗證JSON中的name屬性值。
  • assert json.inception == 2017:驗證JSON中的inception屬性值。

XML檔案處理

Scenario: Read XML
* def xml = read('../xml-example.xml')
* match xml.framework.name == "Karate"
* assert xml.framework.inception == 2017

內容解密:

  • read('../xml-example.xml'):讀取XML檔案並將內容存入xml變數。
  • match xml.framework.name == "Karate":驗證XML中的name屬性值。
  • assert xml.framework.inception == 2017:驗證XML中的inception屬性值。

CSV檔案處理

Scenario: Read CSV
* def csv = read("../csv-example.csv")
* assert csv[0].name == "Karate"
* assert csv[0].inception == 2017

內容解密:

  • read("../csv-example.csv"):讀取CSV檔案並將內容轉換為JSON陣列存入csv變數。
  • assert csv[0].name == "Karate":驗證CSV中的第一行name欄位值。
  • assert csv[0].inception == 2017:驗證CSV中的第一行inception欄位值。

更進階的 Karate 功能

Karate 的斷言與之前的非常相似。值得注意的是,在讀取值時,由於內部將結構表示為 JSON 陣列,因此需要指定行的索引。我們可以透過在讀取檔案後新增 print csv 行來驗證這一點:

讀取 CSV 檔案

08:55:03.823 [main] INFO com.intuit.karate - [print] [
  {
    "name": "Karate",
    "inception": "2017"
  }
]

讓我們繼續討論兩種不同的檔案格式:文字和 JavaScript。

讀取 YAML 檔案

我們有一個名為 yaml-example.yml 的簡單 YAML 檔案,內容如下:

framework:
  name: Karate
  inception: 2017

同樣,我們可以直接存取屬性,因為 Karate 將其轉換為 JSON 結構:

Scenario: Read YAML
  * def yaml = read('../yaml-example.yaml')
  * assert yaml.framework.name == "Karate"
  * assert yaml.framework.inception == 2017

當然,就像之前的其他示例一樣,另一種進行值檢查的方法是使用 match 陳述式,例如:

* match yaml == {'framework': {'name': 'Karate', 'inception': 2017}}

這也適用於智慧 JSON 轉換!

將 YAML 和 CSV 轉換為 JSON

我們已經看到 Karate 自動將 YAML 和 CSV 檔案轉換為 JSON,以便更有效地處理這些格式。在建立這些格式的字串時,也可以使用類別似的轉換,但必須明確執行此操作。

將 YAML 轉換為 JSON 示例

Scenario: Convert YAML to JSON
  * text yaml =
    """
    publisher: Packt
    book:
      title: API testing with the Karate framework
    """
  * yaml converted = yaml
  * print converted

將 CSV 轉換為 JSON 示例

Scenario: Convert CSV to JSON
  * text csv =
    """
    publisher,book
    Packt,API testing with the Karate framework
    """
  * csv converted = csv
  * print converted

讀取文字檔案

文字檔案可以包含幾乎任何型別的資訊,並且沒有特定的結構。因此,這些檔案無法自動轉換為 JSON,並且保持為字串。

讀取文字檔案示例

Scenario: Read text
  * def text = read("../text-example.txt")
  * assert text == "Karate had its first release in 2017."

讀取 JavaScript 檔案

JavaScript 檔案在 Karate 中很特殊,因為它們可以用於儲存可在測試場景中使用的功能。

從 JavaScript 檔案讀取函式示例

Scenario: Read Javascript
  * def jsFunctionFromFile = read("../js-example.js")
  * def text = jsFunctionFromFile("Karate", 2017)
  * assert text == "Karate had its first release in 2017."

在一個 JavaScript 檔案中定義多個函式

由於 Karate 將 JavaScript 檔案中的第一個函式分配給變數,因此除非建立包含所需所有功能的包裝函式,否則無法透過這種方式存取同一檔案中的多個函式。

多函式 JavaScript 檔案示例

function() {
  return {
    getRelease: function(name, year){
      return name + " had its first release in " + year + ".";
    },
    secondMethod: function(){ return 123; }
  };
}

使用多函式 JavaScript 檔案示例

Scenario: Read JavaScript multiple methods
  * def f = read("../js-example2.js")
  * def text = f().getRelease('Karate', 2017)
  * print text
  * def number = f().secondMethod()
  * print number

使用 karate-config.js 中的函式

由於 karate-config.js 檔案在所有測試執行之前自動執行,因此不僅具有值的屬性,還具有包含函式的屬性,都可供場景使用。

karate-config.js 中的函式示例

function fn() {
  config = {
    function1: function(){ return "Cool function 1"; },
    function2: function(){ return "Cool function 2"; }
  };
  return config;
}

使用 karate-config.js 中的函式示例

Scenario: Methods from karate-config.js
  * def f1 = function1()