Skip to main content
Version: Latest (4.6.0)

Implementing Tests Guide

Create ServiceNow Automated Test Framework (ATF) test cases using Fluent APIs across 11 ATF categories: server, form, REST, catalog, email, app navigator, reporting, responsive dashboard, and Service Portal variants (form_SP, catalog_SP). This guide covers test strategy, category selection, step configuration, and the full API surface for each ATF namespace.

When to Use

  • Generating automated test cases for a ServiceNow application
  • Testing forms, APIs, catalog items, dashboards, or server-side logic
  • Building end-to-end workflow tests combining multiple ATF categories
  • Validating email notifications, report visibility, or navigation menus

Instructions

Strategic Approach

  1. Analyze the application context -- examine custom tables, forms, APIs, catalog items, dashboards, and business logic.
  2. Develop a test strategy -- propose up to 3 representative test cases reflecting critical workflows before expanding coverage.
  3. Select ATF categories -- map each test interaction to the appropriate atf.* namespace (see table below).
  4. Implement test steps using the category-specific Fluent ATF APIs.

Category Selection

Interaction typeATF namespaceUse for
UI navigationatf.applicationNavigatorVerify menus/modules visible, navigate to modules
Form interactionsatf.formOpen/submit forms, set/validate fields, click UI actions
Forms in Service Portalatf.form_SPSame as form but in Service Portal context
REST API validationatf.restSend HTTP requests, assert status codes/headers/payload
Server-side logicatf.serverImpersonation, CRUD operations, record validation, logging
Service Catalogatf.catalogOpen/order catalog items, set/validate variables
Catalog in Service Portalatf.catalog_SPSame as catalog but in portal -- plus order guides and multi-row variable sets
Email testingatf.emailValidate outbound emails, generate inbound emails
Reportingatf.reportingAssert report visibility
Dashboardsatf.responsiveDashboardAssert dashboard visibility and sharing

Test File Structure

Every ATF test file must:

import { Test } from "@servicenow/sdk/core";
import "@servicenow/sdk/global";

Test({
$id: Now.ID['test_id'],
name: 'test name',
description: 'optional description',
failOnServerError: true
}, (atf) => {
// Steps execute sequentially
atf.<category>.<method>({
$id: Now.ID['step_id'],
...params
});
});
  • $id must be globally unique for both the test and each step.
  • Steps execute sequentially -- capture earlier step outputs in variables to pass to later steps.

Category Selection Guidance

  • Prefer UI-based categories (atf.form, atf.catalog) over atf.server for interactions that users normally perform through the UI.
  • Use atf.server only when backend assertions, data setup, or server-only operations are needed.
  • When the user mentions Service Portal, use the _SP variants (atf.form_SP, atf.catalog_SP).
  • Combine categories within a single test for end-to-end workflows.

Key Concepts

  • Test data setup: Use atf.server.impersonate and atf.server.createUser to establish user context. Use atf.server.recordInsert to create prerequisite data.
  • Assertion chaining: After atf.form.submitForm, follow with atf.server.recordValidation to verify the record was created correctly server-side.
  • Form UI flavors: standard_ui, service_operations_workspace, asset_workspace, cmdb_workspace.
  • Navigator styles: ui15, ui16, polaris.
  • Catalog variable format: IO:<sys_id>=<value> joined with ^ and ending with ^EQ.
  • Encoded queries: Field value conditions use ServiceNow encoded query syntax (e.g., short_description=Test^priority=1).

API Reference: atf.server

Methods

MethodDescriptionKey Output
impersonateImpersonate a user for the test{ user }
createUserCreate a user with roles and groups{ user }
logLog a message to test resultsvoid
recordQueryQuery records with encoded query{ table, first_record }
recordInsertInsert a record{ table, record_id }
recordValidationValidate record meets conditionsvoid
recordUpdateUpdate a record's fieldsvoid
recordDeleteDelete a recordvoid
searchForCatalogItemSearch catalog items{ catalog_item_id }
checkoutShoppingCartCheckout cart{ request_id }
replayRequestItemReplay a previous request item{ table, req_item }

impersonate

NameTypeMandatoryDescription
userstring | Record<'sys_user'>YesUser to impersonate

createUser

NameTypeMandatoryDescription
firstNamestringYesFirst name
lastNamestringYesLast name
fieldValuesPartial<Data<'sys_user'>>YesAdditional user fields (JSON)
groupsArray<string>YesGroup sys_ids
rolesArray<string>YesRole sys_ids
impersonatebooleanYesWhether to impersonate after creation

recordInsert / recordUpdate

NameTypeMandatoryDescription
tableTableNameYesTarget table
fieldValuesPartial<Data<T>>YesField-value map (snake_case keys)
assertstringNo'record_successfully_inserted' / 'record_not_inserted' / 'record_successfully_updated' / 'record_not_updated'
enforceSecuritybooleanNoDefault: true
recordIdstringYes (update only)sys_id of record to update

recordValidation

NameTypeMandatoryDescription
tableTableNameYesTable to validate against
recordIdstringYessys_id of record
fieldValuesstringYesEncoded query condition
assertstringNo'record_validated' / 'record_not_found'

API Reference: atf.form

Methods

openNewForm, openExistingRecord, submitForm, setFieldValue, fieldValueValidation, fieldStateValidation, uiActionVisibility, clickUIAction, clickModalButton, declarativeActionVisibility, clickDeclarativeAction

Key Properties

openNewForm: table (required), view, formUI (default: "standard_ui")

setFieldValue: table (required), fieldValues (required, JSON object), formUI

submitForm: assert ("", "form_submitted_to_server", "form_submission_canceled_in_browser"), formUI. Returns { table, record_id }.

fieldValueValidation: table, conditions (encoded query), formUI

fieldStateValidation: table, visible[], notVisible[], readOnly[], notReadOnly[], mandatory[], notMandatory[], formUI

clickUIAction: table, uiAction (sys_id), assert, actionType ("ui_action" or "declarative_action"), formUI

API Reference: atf.rest

Methods

sendRestRequest, assertStatusCodeName, assertStatusCode, assertResponseTime, assertResponseHeader, assertResponsePayload, assertResponseJSONPayloadIsValid, assertJsonResponsePayloadElement, assertResponseXMLPayloadIsWellFormed, assertXMLResponsePayloadElement

sendRestRequest

NameTypeMandatoryDescription
pathstringYesAPI path (e.g., /api/now/table/incident)
bodystringYesJSON string request body
authstringYes'basic', 'mutual', or ''
methodstringNo'get', 'post', 'put', 'delete', 'patch'
queryParametersobjectNoKey-value query params
headersobjectNoKey-value request headers

Assert Methods

  • assertStatusCode: statusCode (number), operation ('equals', 'not_equals', 'less_than', etc.)
  • assertResponsePayload: responseBody (string), operation ('contains', 'equals', etc.)
  • assertJsonResponsePayloadElement: elementName (JSON path), elementValue, operation

API Reference: atf.catalog

Methods

openCatalogItem, addItemToShoppingCart, setCatalogItemQuantity, orderCatalogItem, validatePriceAndRecurringPrice, validateVariableValue, variableStateValidation, setVariableValue, openRecordProducer, submitRecordProducer

Key Properties

openCatalogItem: catalogItem (sys_id, required)

setVariableValue: catalogItem (sys_id), variableValues (format: IO:<sys_id>=<value>^IO:<sys_id>=<value>^EQ)

orderCatalogItem: assert ('form_submitted_to_server' or 'form_submission_cancelled_in_browser'). Returns { request_id, cart }.

Important sequencing: openCatalogItem must precede orderCatalogItem. openRecordProducer must precede submitRecordProducer.

API Reference: atf.email

Methods

MethodDescription
validateOutboundEmailFilter sys_email table for sent emails
validateOutboundEmailGeneratedByNotificationFilter by notification source
validateOutboundEmailGeneratedByFlowFilter by flow source
generateInboundEmailGenerate a new inbound email
generateInboundReplyEmailGenerate an inbound reply
generateRandomStringGenerate test data string

generateInboundEmail

from, to, subject, body (all required strings). Returns { output_email_record }.

API Reference: atf.applicationNavigator

Methods

  • moduleVisibility: Check if modules are visible in navigation. navigator ('ui15', 'ui16', 'polaris'), visibleModules[], notVisibleModules[].
  • navigateToModule: Navigate to a module. module (sys_id).
  • applicationMenuVisibility: Check if app menus are visible. visible[], notVisible[].

API Reference: atf.reporting

reportVisibility

report (sys_id of sys_report), assert ('can_view_report' or 'cannot_view_report').

API Reference: atf.responsiveDashboard

responsiveDashboardVisibility

dashboard (sys_id of pa_dashboards), assert ('dashboard_is_visible' or 'dashboard_is_not_visible').

responsiveDashboardSharing

dashboard (sys_id), assert ('can_share_dashboard' or 'cannot_share_dashboard').

Service Portal Variants

atf.form_SP

Same methods as atf.form with additional portal and page properties plus openServicePortalPage method. Uses form_SP namespace.

atf.catalog_SP

Same methods as atf.catalog with additional portal and page properties, plus: openOrderGuide, navigatewithinOrderGuide, validateOrderGuideItem, reviewOrderGuideSummary, saveCurrentRowOfMultiRowVariableSet, addRowToMultiRowVariableSet.

Avoidance

  1. Do not overuse atf.server for tasks that form or catalog APIs handle directly.
  2. Do not hardcode sys_id values -- always look them up.
  3. Do not skip mandatory fields when using setFieldValue or recordInsert.
  4. Do not call sequence-dependent steps out of order.
  5. Do not create generic or template-based tests -- each test should reflect real usage scenarios.

Example: End-to-End Form Test

import "@servicenow/sdk/global";
import { Test } from "@servicenow/sdk/core";

Test({
$id: Now.ID["validate_incident_form"],
name: "Create and Validate Incident",
description: "Opens a new incident form, sets fields, submits, and validates",
failOnServerError: true
}, (atf) => {
atf.form.openNewForm({
$id: Now.ID["open_new_incident"],
table: "incident",
formUI: "standard_ui"
});

atf.form.setFieldValue({
$id: Now.ID["set_fields"],
table: "incident",
fieldValues: {
short_description: "Email server is down"
},
formUI: "standard_ui"
});

const result = atf.form.submitForm({
$id: Now.ID["submit_form"],
assert: "form_submitted_to_server",
formUI: "standard_ui"
});

atf.server.recordValidation({
$id: Now.ID["validate_record"],
table: "incident",
recordId: result.record_id,
fieldValues: "short_description=Email server is down",
assert: "record_validated"
});
});

Example: REST API Test

import "@servicenow/sdk/global";
import { Test } from "@servicenow/sdk/core";

Test({
$id: Now.ID["scaffold_api_test"],
name: "Scaffold API Test",
failOnServerError: true
}, (atf) => {
atf.rest.sendRestRequest({
$id: Now.ID["send_request"],
path: "/api/now/fluent/scaffold",
body: "",
auth: "basic",
method: "get",
queryParameters: { new: "true" },
headers: {}
});

atf.rest.assertStatusCode({
$id: Now.ID["assert_status"],
operation: "equals",
statusCode: 200
});

atf.rest.assertResponseJSONPayloadIsValid({
$id: Now.ID["assert_json_valid"]
});

atf.rest.assertJsonResponsePayloadElement({
$id: Now.ID["assert_result"],
elementName: "result",
operation: "equals",
elementValue: "success"
});
});