atf-testing について
ServiceNow の Automated Test Framework(ATF)テストとテストスイートを作成・実行するスキルです。UI とサーバーサイドのステップ、アサーション、パラメーター化、データの後片付けを扱い、回帰テストや継続的インテグレーションに役立ちます。
主な機能
- 01テストとテストスイートの作成
- 02UI とサーバーサイドのステップ追加
- 03フィールド値と結果の検証
- 04テストのパラメーター化
- 05テストデータの準備と後片付け
活用シーン
- 01インシデントの作成と更新をテスト
- 02アプリの回帰テストスイートを実行
- 03異なるユーザーロールでの動作を検証
MCP 対応
ATF テスト、ステップ、スイートの作成、テスト実行、結果取得、既存テストの検索に使う MCP ツールを説明します。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill atf-testingインストール後、Claude Code で/atf-testingを入力して読み込まれたか確認します。
詳細
ServiceNow 向け自動テストフレームワーク(ATF)
ATF は ServiceNow アプリケーション向けの自動テスト機能を提供し、回帰テストと継続的インテグレーションを可能にします。
ATF のアーキテクチャ
テストの階層
Test Suite: Incident Management Tests
├── Test: Create Incident
│ ├── Step 1: Impersonate User
│ ├── Step 2: Open New Record
│ ├── Step 3: Set Field Values
│ ├── Step 4: Submit Form
│ └── Step 5: Assert Values
├── Test: Assign Incident
│ └── Steps...
└── Test: Resolve Incident
└── Steps...
主要なテーブル
| テーブル | 用途 |
|---|---|
sys_atf_test | テスト定義 |
sys_atf_step | 個々のテストステップ |
sys_atf_test_suite | テストスイートのグループ |
sys_atf_test_suite_test | スイートとテストの関係 |
sys_atf_test_result | テスト実行結果 |
テストステップの種類
一般的なステップの種類
| ステップの種類 | 用途 | 例 |
|---|---|---|
| Impersonate | 特定のユーザーとして実行 | ITIL ユーザーとしてテスト |
| Open a New Form | 新しいレコードを作成 | 新しいインシデントフォーム |
| Open an Existing Form | レコードを編集 | INC0010001 を開く |
| Set Field Values | フィールドに値を入力 | 優先度と説明を設定 |
| Click a UI Action | ボタンを実行 | 「Save」をクリック |
| Assert Field Values | 値を検証 | 優先度 = 1 |
| Run Server Side Script | スクリプトを実行 | カスタム検証 |
| Wait | 実行を一時停止 | 非同期処理を待機 |
テストの作成
基本的なテスト構造(ES5)
// Create a new ATF test
var test = new GlideRecord("sys_atf_test")
test.initialize()
test.setValue("name", "Test: Create High Priority Incident")
test.setValue("description", "Verify high priority incident creation workflow")
test.setValue("active", true)
test.setValue("application", "global") // or scoped app sys_id
var testSysId = test.insert()
// Add test steps
function addTestStep(testId, order, stepType, config) {
var step = new GlideRecord("sys_atf_step")
step.initialize()
step.setValue("test", testId)
step.setValue("order", order)
step.setValue("step_config", stepType)
// Set step-specific configuration
for (var key in config) {
if (config.hasOwnProperty(key)) {
step.setValue(key, config[key])
}
}
return step.insert()
}
Impersonate ステップ
// Step 1: Impersonate ITIL user
addTestStep(testSysId, 100, "sys_atf_step_config_impersonate", {
description: "Impersonate ITIL user",
inputs: JSON.stringify({
user: "itil", // or sys_id
}),
})
Open New Form ステップ
// Step 2: Open new incident form
addTestStep(testSysId, 200, "sys_atf_step_config_open_new_form", {
description: "Open new incident form",
inputs: JSON.stringify({
table: "incident",
}),
})
Set Field Values ステップ
// Step 3: Set field values
addTestStep(testSysId, 300, "sys_atf_step_config_set_field_values", {
description: "Set incident fields",
inputs: JSON.stringify({
table: "incident",
values: [
{ field: "short_description", value: "ATF Test Incident" },
{ field: "priority", value: "1" },
{ field: "category", value: "network" },
{ field: "caller_id", value: "admin" },
],
}),
})
Submit Form ステップ
// Step 4: Submit form (click Save)
addTestStep(testSysId, 400, "sys_atf_step_config_click_ui_action", {
description: "Save the incident",
inputs: JSON.stringify({
ui_action: "sysverb_insert", // Submit/Insert action
}),
})
Assert Field Values ステップ
// Step 5: Assert values
addTestStep(testSysId, 500, "sys_atf_step_config_assert_field_values", {
description: "Verify incident was created correctly",
inputs: JSON.stringify({
table: "incident",
assertions: [
{
field: "priority",
operator: "equals",
value: "1",
},
{
field: "state",
operator: "equals",
value: "1", // New
},
{
field: "number",
operator: "is not empty",
},
],
}),
})
サーバーサイドスクリプトのステップ
カスタム検証スクリプト(ES5)
// Step: Run Server Side Script
// Script (ES5 only!):
;(function (outputs, steps, params, stepResult) {
// Access outputs from previous steps
var incidentSysId = steps["step_sys_id"].record_id
// Perform custom validation
var gr = new GlideRecord("incident")
if (gr.get(incidentSysId)) {
// Check business rule fired
if (gr.getValue("assignment_group") === "") {
stepResult.setOutputMessage("Assignment group not set by business rule")
stepResult.setFailed()
return
}
// Check SLA attached
var sla = new GlideRecord("task_sla")
sla.addQuery("task", incidentSysId)
sla.query()
if (!sla.hasNext()) {
stepResult.setOutputMessage("No SLA attached to incident")
stepResult.setFailed()
return
}
// All validations passed
outputs.incident_number = gr.getValue("number")
outputs.sla_count = sla.getRowCount()
stepResult.setOutputMessage("All validations passed")
} else {
stepResult.setOutputMessage("Incident not found")
stepResult.setFailed()
}
})(outputs, steps, params, stepResult)
データセットアップスクリプト(ES5)
// Step: Setup test data
;(function (outputs, steps, params, stepResult) {
// Create test user if needed
var user = new GlideRecord("sys_user")
user.addQuery("user_name", "atf_test_user")
user.query()
if (!user.next()) {
user.initialize()
user.setValue("user_name", "atf_test_user")
user.setValue("first_name", "ATF")
user.setValue("last_name", "Test User")
user.setValue("email", "[email protected]")
user.setValue("active", true)
outputs.user_sys_id = user.insert()
} else {
outputs.user_sys_id = user.getUniqueValue()
}
stepResult.setOutputMessage("Test user ready: " + outputs.user_sys_id)
})(outputs, steps, params, stepResult)
クリーンアップスクリプト(ES5)
// Step: Cleanup test data (always runs)
;(function (outputs, steps, params, stepResult) {
var testRecordId = steps["create_incident_step"].record_id
if (testRecordId) {
var gr = new GlideRecord("incident")
if (gr.get(testRecordId)) {
gr.deleteRecord()
stepResult.setOutputMessage("Cleaned up test incident: " + testRecordId)
}
}
})(outputs, steps, params, stepResult)
テストスイート
テストスイートの作成
// Create test suite
var suite = new GlideRecord("sys_atf_test_suite")
suite.initialize()
suite.setValue("name", "Incident Management Regression Suite")
suite.setValue("description", "Full regression tests for incident management")
suite.setValue("active", true)
var suiteSysId = suite.insert()
// Add tests to suite
function addTestToSuite(suiteId, testId, order) {
var link = new GlideRecord("sys_atf_test_suite_test")
link.initialize()
link.setValue("test_suite", suiteId)
link.setValue("test", testId)
link.setValue("order", order)
return link.insert()
}
addTestToSuite(suiteSysId, createTestId, 100)
addTestToSuite(suiteSysId, assignTestId, 200)
addTestToSuite(suiteSysId, resolveTestId, 300)
パラメーター化されたテスト
テストパラメーターの使用
// Test with parameters
var test = new GlideRecord('sys_atf_test');
test.initialize();
test.setValue('name', 'Test: Create Incident with Priority');
test.setValue('parameters', JSON.stringify({
priority: '2',
category: 'software'
}));
test.insert();
// In step, reference parameter
{
"values": [
{ "field": "priority", "value": "${priority}" },
{ "field": "category", "value": "${category}" }
]
}
MCP ツールとの連携
利用可能な ATF ツール
| ツール | 用途 |
|---|---|
snow_create_atf_test | テストを作成 |
snow_create_atf_test_step | テストにステップを追加 |
snow_create_atf_test_suite | スイートを作成 |
snow_execute_atf_test | テストを実行 |
snow_get_atf_results | 結果を取得 |
snow_discover_atf_tests | 既存のテストを検索 |
ワークフローの例
// 1. Create test
var testId = await snow_create_atf_test({
name: "Test: Incident Priority Escalation",
description: "Verify priority changes trigger notifications",
})
// 2. Add steps
await snow_create_atf_test_step({
test_id: testId,
order: 100,
type: "impersonate",
user: "itil",
})
await snow_create_atf_test_step({
test_id: testId,
order: 200,
type: "server_script",
script: createIncidentScript,
})
// 3. Execute test
var resultId = await snow_execute_atf_test({
test_id: testId,
})
// 4. Get results
var results = await snow_get_atf_results({
result_id: resultId,
})
ベストプラクティス
- テストデータを分離する - 各テストでテストデータを作成し、クリーンアップします
- なりすましを使用する - 実際のユーザーロールとしてテストします
- アトミックなテスト - 各テストで 1 つのシナリオを検証します
- わかりやすい名前 - テストとステップの説明を明確にします
- ステップの順序付け - 挿入しやすいように 100、200、300 を使用します
- 非同期処理に対応する - 非同期操作には待機ステップを追加します
- 必ずクリーンアップする - クリーンアップには finally ステップを使用します
- パラメーター化する - 再利用可能なテストにはパラメーターを使用します
一般的なアサーション
| アサーション | 用途 |
|---|---|
equals | 値の完全一致 |
not equals | 値の除外 |
is empty | フィールドが空であること |
is not empty | フィールドに値があること |
contains | 部分文字列の一致 |
starts with | 接頭辞の一致 |
greater than | 数値の比較 |
less than | 数値の比較 |
コメント
Markdown 対応