第4章:runnビルトイン関数編¶
runnは標準的なexpr-lang関数に加えて、テストシナリオ用の独自ビルトイン関数を提供しています。これらの関数により、テストシナリオの記述が容易になります。
ビルトイン関数の利点¶
通常のプログラミング言語でテストを書く場合の課題:
- 差分比較が見にくい
- テストデータ作成に手間がかかる
- ファイル操作が複雑
- 対話的なテストが難しい
runnのビルトイン関数は、これらの課題を解決します。
runnビルトイン関数一覧¶
以下に主要なビルトイン関数を紹介します。
関数名 | 用途 | 例 |
---|---|---|
urlencode |
URLパラメータをエンコード | urlencode("検索 キーワード") → %E6%A4%9C%E7%B4%A2%20%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89 |
bool |
文字列や数値を真偽値に変換 | bool("1") → true 、bool("") → false |
compare |
レスポンスの厳密な比較 | compare(response, expected) |
diff |
差分の表示 | diff(actual, expected) |
pick |
必要なフィールドの抽出 | pick(user, "id", "name") |
omit |
不要なフィールドの除外 | omit(response, "timestamp", "requestId") |
merge |
複数のオブジェクトを合成 | merge(defaults, overrides) |
intersect |
配列の共通要素を取得 | intersect(tagsA, tagsB) |
input |
対話的な入力 | input("APIキーを入力してください:") |
secret |
パスワードの安全な入力 | secret("パスワード:") |
select |
選択肢からの選択 | select("環境を選択:", ["dev","prod"], "dev") |
basename |
パスからファイル名を取得 | basename("/uploads/image.jpg") → image.jpg |
time |
時刻の統一処理 | time("2024-01-01") |
faker.* |
テストデータの自動生成 | faker.Name() → "田中太郎" |
file |
ファイル内容の読み込み | file("./testdata.json") |
urlencode関数¶
日本語や特殊文字を含むパラメータを正しくエンコードするための関数です。
desc: urlencode 関数の使用例
steps:
urlencode_example:
dump: |
urlencode("Hello, World!")
結果:
Hello%2C+World%21
bool関数¶
APIレスポンスの文字列や数値を真偽値として扱いたい場合に使用します。
desc: bool関数の使用例
vars:
string_true: "true"
string_false: "false"
number_one: 1
number_zero: 0
empty_string: ""
steps:
bool_example:
desc: 様々な値を真偽値に変換
bind:
results:
string_true: bool(vars.string_true) # true
string_false: bool(vars.string_false) # false
number_one: bool(vars.number_one) # true
number_zero: bool(vars.number_zero) # false
empty_string: bool(vars.empty_string) # false
test: |
current.results.string_true == true &&
current.results.string_false == false &&
current.results.number_one == true &&
current.results.number_zero == false &&
current.results.empty_string == false
変換ルール:
- 文字列:
"true"
→true
、"false"
や空文字 →false
- 数値:
1
→true
、0
→false
- その他: 値が存在すれば
true
、null や空ならfalse
compare関数¶
2つの値を厳密に比較し、差分があればテストを失敗させて詳細を表示します。
desc: compare関数の基本的な使用例
vars:
# 比較用のデータ
expected:
name: "Alice"
age: 30
city: "Tokyo"
actual:
name: "Alice"
age: 31
country: "Japan"
steps:
compare_example:
test: |
// compare関数で差分を検出
compare(vars.expected, vars.actual)
compare_with_ignore:
test: |
// compare関数で差分を検出
compare(vars.expected, vars.actual, ['.name'])
結果:
1) examples/runn-builtins/compare_basic.fail.yml 880734e90dcb1377eb0012893664be1268a746da
Failure/Error: test failed on "compare関数の基本的な使用例".steps.compare_example: condition is not true
Condition:
// compare関数で差分を検出
compare(vars.expected, vars.actual)
│
├── (diff) => map[string]any{
│ - "age": float64(30),
│ + "age": float64(31),
│ - "city": string("Tokyo"),
│ + "country": string("Japan"),
│ "name": string("Alice"),
│ }
├── vars.expected => {"age":30,"city":"Tokyo","name":"Alice"}
└── vars.actual => {"age":31,"country":"Japan","name":"Alice"}
Failure step (examples/runn-builtins/compare_basic.fail.yml):
16 compare_example:
17 test: |
18 // compare関数で差分を検出
19 compare(vars.expected, vars.actual)
1 scenario, 0 skipped, 1 failure
diff関数¶
差分を色付きで見やすく表示する関数です。
desc: diff関数の使用例
vars:
# 差分を取るためのテキストデータ
old_text: "Hello\nWorld\nTest"
new_text: "Hello\nPlanet\nTest"
steps:
string_diff_example:
# テキストの差分
dump: diff(vars.old_text, vars.new_text)
json_diff_example:
# データ構造の差分
dump: |
diff(
{"users": ["Alice", "Bob"]},
{"users": ["Alice", "Charlie"], "count": 2}
)
結果:
string(
"Hello\nWorld\nTest",
"Hello\nPlanet\nTest",
)
map[string]any{
"count": float64(2),
"users": []any{
string("Alice"),
string("Bob"),
string("Charlie"),
},
}
pick関数¶
オブジェクトから必要なフィールドだけを抽出する関数です。
desc: pick関数の使用例
vars:
user:
id: 1
name: "Alice"
email: "alice@example.com"
password: "secret"
created_at: "2024-01-01"
steps:
pick_example:
dump: |
// パスワードを除外してユーザー情報を抽出
pick(vars.user, "id", "name", "email")
結果:
{
"email": "alice@example.com",
"id": 1,
"name": "Alice"
}
omit関数¶
オブジェクトから不要なフィールドを除外する関数です。タイムスタンプやリクエストIDなど、テストで無視したいフィールドがある場合に便利です。
desc: omit関数の使用例
vars:
user:
id: 1
name: "Alice"
email: "alice@example.com"
password: "secret"
created_at: "2024-01-01"
steps:
omit_example:
dump: |
// パスワードを除外してユーザー情報を抽出
omit(vars.user, "id", "name", "email")
結果:
{
"created_at": "2024-01-01",
"password": "secret"
}
merge関数¶
複数のオブジェクトを合成する関数です。デフォルト設定に一部だけ上書きしたい場合などに使用します。
desc: merge関数の使用例
vars:
defaults:
timeout: 30
retries: 3
debug: false
custom:
timeout: 60
verbose: true
steps:
merge_example:
dump: |
// デフォルト設定とカスタム設定をマージ
merge(vars.defaults, vars.custom)
結果:
{
"debug": false,
"retries": 3,
"timeout": 60,
"verbose": true
}
intersect関数¶
配列の共通要素を見つける関数です。
desc: intersect関数の使用例 - 2つの配列の共通要素を取得
vars:
fruits1: ["apple", "banana", "orange", "grape"]
fruits2: ["banana", "grape", "melon", "apple"]
nums1: [1, 2, 3, 4, 5]
nums2: [3, 4, 5, 6, 7]
strings1: ["hello", "world", "foo", "bar"]
strings2: ["foo", "bar", "baz", "hello"]
steps:
intersect_example:
dump: |
{
"fruits_common": intersect(vars.fruits1, vars.fruits2),
"numbers_common": intersect(vars.nums1, vars.nums2),
"strings_common": intersect(vars.strings1, vars.strings2)
}
結果:
{
"fruits_common": [
"apple",
"banana",
"grape"
],
"numbers_common": [
3,
4,
5
],
"strings_common": [
"hello",
"foo",
"bar"
]
}
input関数¶
テスト実行時に対話的に値を入力できる関数です。
desc: input関数の使用例
steps:
-
bind:
id: input("Enter your ID", "default")
-
dump: id
secret関数¶
パスワードなどの機密情報を安全に入力するための関数です。入力内容は画面に表示されません。
desc: secret関数の使用例 - パスワードをセキュアに入力
steps:
-
bind:
# パスワードを安全に入力(入力時は表示されない)
password: secret("Enter your password")
-
dump: |
{
"message": "Password has been set securely",
"length": len(password)
}
select関数¶
実行時に選択肢から値を選択できる関数です。
desc: select関数を使った対話的な選択
steps:
select_environment:
desc: 環境を選択
# 3つの引数:メッセージ、選択肢リスト、デフォルト値
dump: select("どの環境にデプロイしますか? (development/staging/production)", ["development", "staging", "production"], "development")
select_without_default:
desc: デフォルトなしの選択
# デフォルト値を空文字列にすると必須選択になる
dump: select("好きな色を選んでください (red/blue/green/yellow/purple):", ["red", "blue", "green", "yellow", "purple"], "")
basename関数¶
ファイルパスからファイル名を取得する関数です。
desc: basename関数でファイルパスからファイル名を取得
steps:
simple_basename:
desc: 単純なファイルパスからファイル名を取得
dump: basename("/home/user/documents/report.pdf")
unix_path:
desc: Unixパスからファイル名を取得
dump: basename("/var/log/nginx/access.log")
windows_path:
desc: Windowsパスからファイル名を取得
dump: basename("C:\\Users\\Documents\\data.xlsx")
filename_only:
desc: ファイル名だけの場合
dump: basename("config.yml")
trailing_slash:
desc: 末尾にスラッシュがある場合(最後のディレクトリ名を返す)
dump: basename("/path/to/directory/")
empty_path:
desc: 空のパスの場合(ドットを返す)
dump: basename("")
dot_file:
desc: ドットファイルの場合
dump: basename("/home/user/.bashrc")
結果:
report.pdf
access.log
C:\Users\Documents\data.xlsx
config.yml
directory
.
.bashrc
time関数¶
様々な形式の日時文字列を統一的に扱うための関数です。
desc: time関数で文字列や数値を時刻に変換
steps:
from_string_rfc3339:
desc: RFC3339形式の文字列を時刻に変換
dump: time("2024-01-15T10:30:00Z")
from_string_datetime:
desc: 日時文字列を時刻に変換
dump: time("2024-01-15 10:30:00")
from_string_date:
desc: 日付文字列を時刻に変換
dump: time("2024-01-15")
from_unix_timestamp:
desc: Unixタイムスタンプ(秒)を時刻に変換
dump: time(1705320600)
various_formats:
desc: 様々なフォーマットの変換
dump: |
{
"slash_date": time("2024/01/15"),
"us_date": time("January 15, 2024"),
"with_timezone": time("2024-01-15 10:30:00 +0900")
}
結果:
"2024-01-15T10:30:00Z"
"2024-01-15T10:30:00Z"
"2024-01-15T00:00:00Z"
"2024-01-15T12:10:00Z"
{
"slash_date": "2024-01-15T00:00:00Z",
"us_date": "2024-01-15T00:00:00Z",
"with_timezone": "2024-01-15T10:30:00+09:00"
}
faker関数群¶
リアルで多様なテストデータを自動生成する関数群です。
desc: faker関数でテストデータを生成 - 全メソッド網羅版
steps:
person_data:
desc: 人物データの生成
dump: |
{
"name": faker.Name(),
"firstName": faker.FirstName(),
"lastName": faker.LastName(),
"email": faker.Email(),
"username": faker.Username()
}
auth_data:
desc: 認証関連データの生成
dump: |
{
"username": faker.Username(),
"password_all": faker.Password(true, true, true, true, true, 20),
"password_lower_only": faker.Password(true, false, false, false, false, 10),
"password_upper_only": faker.Password(false, true, false, false, false, 10),
"password_numeric_only": faker.Password(false, false, true, false, false, 10),
"password_special_only": faker.Password(false, false, false, true, false, 10),
"password_with_space": faker.Password(true, true, true, false, true, 15)
}
misc_data:
desc: その他の基本データ
dump: |
{
"bool": faker.Bool(),
"uuid": faker.UUID()
}
uuid_variants:
desc: UUID各バージョンとULID
dump: |
{
"uuidv4": faker.UUIDv4(),
"uuidv6": faker.UUIDv6(),
"uuidv7": faker.UUIDv7(),
"ulid": faker.ULID()
}
color_data:
desc: 色関連データの生成
dump: |
{
"color": faker.Color(),
"hexColor": faker.HexColor()
}
internet_data:
desc: インターネット関連データの生成
dump: |
{
"url": faker.URL(),
"domain": faker.Domain(),
"ipv4": faker.IPv4(),
"ipv6": faker.IPv6(),
"httpStatusCode": faker.HTTPStatusCode(),
"httpMethod": faker.HTTPMethod(),
"httpVersion": faker.HTTPVersion(),
"userAgent": faker.UserAgent()
}
datetime_data:
desc: 日時関連データの生成
dump: |
{
"date": faker.Date(),
"nanoSecond": faker.NanoSecond(),
"second": faker.Second(),
"minute": faker.Minute(),
"hour": faker.Hour(),
"month": faker.Month(),
"day": faker.Day(),
"year": faker.Year()
}
emoji_data:
desc: 絵文字の生成
dump: |
{
"emoji": faker.Emoji()
}
number_data:
desc: 数値データの生成
dump: |
{
"int": faker.Int(),
"intRange_small": faker.IntRange(1, 10),
"intRange_medium": faker.IntRange(100, 1000),
"intRange_large": faker.IntRange(10000, 99999),
"float": faker.Float(),
"floatRange_small": faker.FloatRange(0.0, 1.0),
"floatRange_medium": faker.FloatRange(10.0, 100.0),
"floatRange_large": faker.FloatRange(1000.0, 10000.0)
}
string_data:
desc: 文字列データの生成
dump: |
{
"digit": faker.Digit(),
"digitN_5": faker.DigitN(5),
"digitN_10": faker.DigitN(10),
"digitN_15": faker.DigitN(15),
"letter": faker.Letter(),
"letterN_5": faker.LetterN(5),
"letterN_10": faker.LetterN(10),
"letterN_15": faker.LetterN(15),
"lexify_simple": faker.Lexify("????"),
"lexify_complex": faker.Lexify("TEST-????-????"),
"numerify_phone": faker.Numerify("###-###-####"),
"numerify_code": faker.Numerify("CODE-########")
}
edge_cases:
desc: エッジケースのテスト
dump: |
{
"digitN_0": faker.DigitN(0),
"digitN_negative": faker.DigitN(-1),
"letterN_0": faker.LetterN(0),
"letterN_negative": faker.LetterN(-1),
"intRange_same": faker.IntRange(42, 42),
"floatRange_same": faker.FloatRange(3.14, 3.14)
}
結果:
{
"email": "kaelynkilback@jakubowski.biz",
"firstName": "Bobby",
"lastName": "Wyman",
"name": "Oscar Doyle",
"username": "Kirlin6843"
}
{
"password_all": "D3F4uFJU4aLEln90*7Mz",
"password_lower_only": "zcwvdfpptm",
"password_numeric_only": "6714166839",
"password_special_only": "?_-@#*$_?-",
"password_upper_only": "SEXVDPPMUY",
"password_with_space": "45THfz66XE8gk77",
"username": "Rosenbaum1293"
}
{
"bool": true,
"uuid": "3ba59184-6953-4ef4-92c1-87ad948a9316"
}
{
"ulid": "01K173HV3D0WQ36PEBDSCWSZJH",
"uuidv4": "f02b8075-cb3f-4b05-aa3c-338195d17496",
"uuidv6": "01f06b41-a314-62cf-aa8e-000d3a3d89cf",
"uuidv7": "01984e38-ec6d-77ed-b9c0-7a70be03159e"
}
{
"color": "FireBrick",
"hexColor": "#164f05"
}
{
"domain": "senioropen-source.name",
"httpMethod": "POST",
"httpStatusCode": 301,
"httpVersion": "HTTP/2.0",
"ipv4": "105.229.235.104",
"ipv6": "e468:ef69:659f:645d:e073:bc6f:c4a8:d501",
"url": "https://www.forwardsolutions.net/infrastructures/bricks-and-clicks",
"userAgent": "Mozilla/5.0 (Windows; U; Windows 98) AppleWebKit/535.44.3 (KHTML, like Gecko) Version/6.0 Safari/535.44.3"
}
{
"date": "2013-08-10T09:54:41.276197782Z",
"day": 8,
"hour": 16,
"minute": 19,
"month": 8,
"nanoSecond": 895525813,
"second": 37,
"year": 1963
}
{
"emoji": "🎪"
}
{
"float": 9.56449175256422e+307,
"floatRange_large": 4.577277248245723e+306,
"floatRange_medium": 1.2302172706489237e+308,
"floatRange_small": 1.0202585755633835e+306,
"int": 7482777973562367905,
"intRange_large": 76995,
"intRange_medium": 556,
"intRange_small": 2
}
{
"digit": "6",
"digitN_10": "0789564275",
"digitN_15": "867338934324134",
"digitN_5": "40047",
"letter": "H",
"letterN_10": "QFfSHFPUEK",
"letterN_15": "zlgthMMykPoyIjl",
"letterN_5": "awuZJ",
"lexify_complex": "TEST-RqMn-eTHa",
"lexify_simple": "xIqU",
"numerify_code": "CODE-06996186",
"numerify_phone": "284-628-1882"
}
{
"digitN_0": "2",
"digitN_negative": "",
"floatRange_same": 2.4912506820298563e+307,
"intRange_same": 42,
"letterN_0": "A",
"letterN_negative": ""
}
file関数¶
ファイルの内容を読み込む関数です。
steps:
file_example:
desc: ファイルの内容を読み込む
bind:
content: file("./data.txt")
test: |
// ファイルの内容を検証
content != null && len(content) > 0
使用例:
- 設定ファイルの読み込み
- テストデータの読み込み
- テンプレートファイルの読み込み
- 期待値ファイルとの比較