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
內容解密:
kmToMiles和milesToKm是兩個函式,分別用於公里到英里和英里到公里的轉換。- 使用
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()