Skip to content

AI prompt examples

Seven worked examples of prompts that produce good AutoTestX tests. Each one shows the prompt a tester writes, the test that came out, and what to notice. Every resulting test passes validation and passes when run. Use them as models for your own prompts.

How to write a prompt and which option to choose is explained in the Prompt guide. In short:

  • Option A: Write with AI: plain-language steps typed into the editor. Examples 1 and 2.
  • Option B: an AI chat (Claude or similar), using the guide’s master prompt. It writes a complete test file you import. Examples 3 to 7.
# Option What it shows
1 A A negative check: sign-in refused
2 A A validation message, and a check that nothing was created
3 B A loop over order lines, a remembered value, a check on a list row
4 B A manual test case converted, rows found by PO number
5 B Data-driven: six rows of good and bad credentials
6 B Waiting for the page state instead of a fixed wait
7 B An API test with a negative case and cleanup

The examples use the bundled fixture application (a small purchase-order screen) and the demo API, so you can try them yourself. The test files are in the repository under examples/ai-prompts/; see Trying the examples.


Option A. Typed into Write with AI → Describe the test:

Open the application.
Type {{secret.APP_USER}} into the "Username" field.
Type wrong-password into the "Password" field.
Click the "Sign In" button.
Check that the alert shows exactly "Invalid credentials".
Check that the "Sign In" button is still visible.
Check that the heading "Purchase Orders" is not visible.

Result: 7 steps.

Step Type Target
Open the application Open {{baseUrl}}
Type the test user into Username Type (sensitive) label Username
Type a wrong password into Password Type (sensitive) label Password
Click Sign In Click test id login, then button Sign In
The alert says exactly Invalid credentials Text role alert
The Sign In button is still visible Visible test id login
The Purchase Orders heading is not visible Visible, negated heading Purchase Orders

Notice: the expected message is given exactly, and two more checks prove the user was not let in. “Check that login fails” would produce a weak test.


Option A:

Open the application and sign in with APP_USER and APP_PASSWORD.
Click the "New Purchase Order" button.
Select "Globex" in the "Vendor" dropdown.
Type "Order with no lines" into the "Description" field.
Click "Save" without adding any line.
Check that the status message says exactly "Add at least one line before saving".
Remember the text of the PO number (test id po-number) as poNumber.
Check that poNumber is empty.
Take a screenshot named "save-refused".

Result: 13 steps. The last checks are:

Step Type Detail
The message says a line is needed Text role status, expected Add at least one line before saving
Read the PO number field Extract test id po-number, saved as poNumber
No PO number was generated Expression LENGTH(poNumber) == 0
Screenshot of the refused save Screenshot save-refused

Notice: “check that nothing was created” is the half of a negative test people forget. A Text check cannot expect an empty value, so the prompt says remember … then check it is empty, which becomes an Extract and an Expression step.


Option B. The guide’s master prompt, with its The test to write section filled in like this:

## The test to write
Application / screen: Fixture ERP - Purchase Orders
Environment variables available: baseUrl
Secret names available: APP_USER, APP_PASSWORD
Preconditions: none; the test signs in itself.
Requirement:
As a buyer I can create a purchase order with several lines. On save the system
shows "Saved", generates a number PO-<digits>, sets status Approved, and the
order appears in the Purchase Orders list with status Approved.
Screen details:
<section aria-label="Create Purchase Order">
<label for="vendor_7731">Vendor</label> <select id="vendor_7731"> Acme Ltd | Globex
<label for="desc_7732">Description</label> <input id="desc_7732">
<table> Lines: rows with cells Item | Quantity </table>
<label>Item</label> <input> <label>Quantity</label> <input type="number">
<button data-testid="add-line">Add Item</button>
<button data-testid="save-po">Save</button>
<div role="status"> (shows "Saved") </div>
<span data-testid="po-number"></span> <span data-testid="po-status"></span>
</section>
List: <table role="grid" aria-label="Purchase order list">, one row per order:
PO Number | Vendor | Status | Actions
Test data: vendor Acme Ltd; lines 1234 x 2, 5678 x 1, 7002 x 4
- put the lines in variables and add them with control.forEach.
- after each Add Item, check the line's row shows the quantity.
Expected results: as in the requirement. Status check is soft.
Values to remember: the PO number as poNumber.
Cleanup needed: none (the fixture resets on reload).

Result: a test with the lines as a variable and a For each loop. Its key parts:

"variables": {
"lines": [ { "item": "1234", "qty": "2" }, { "item": "5678", "qty": "1" }, { "item": "7002", "qty": "4" } ]
},
...
{
"id": "line-listed", "type": "assert.text", "label": "The line's row shows the quantity",
"target": {
"scope": { "within": { "role": "row", "key": "{{line.item}}" } },
"strategies": [{ "kind": "css", "value": "td:nth-child(2)" }]
},
"params": { "expected": "{{line.qty}}" }
},
...
{
"id": "in-list", "type": "assert.exists", "label": "The new order's row in the list says Approved",
"target": {
"scope": { "within": { "role": "row", "key": "{{poNumber}}" } },
"strategies": [{ "kind": "text", "value": "Approved" }]
}
}

Notice: the pasted HTML gives the AI real test ids and labels, so no target is a guess. The list check finds the row by its PO number, never “the last row”.


Option B. The master prompt plus the guide’s convert manual test cases variant, with the manual case pasted in:

TC-2001 Approve and delete purchase orders from the list
Precondition: orders PO-100001 (Acme Ltd, Draft) and PO-100002 (Globex, Draft) exist.
| # | Step | Expected result |
|---|---------------------------------------|-----------------------------------------|
| 1 | Open the application | Sign-in screen is shown |
| 2 | Sign in as the test user | Purchase order list is shown |
| 3 | Click Approve on PO-100001 | PO-100001 status becomes Approved |
| 4 | Click Delete on PO-100002 | PO-100002 is removed from the list |
| 5 | - | PO-100001 is still listed, Approved |
Screen details: each list row is <tr role="row"> with cells
PO Number | Vendor | <td class="status"> | <button class="row-approve">Approve</button>
<button class="row-delete">Delete</button>.
Find rows by PO number, never by position. Check each precondition before acting on it.

Result: a test named TC-2001 Approve and delete purchase orders from the list, tagged TC-2001, whose step labels keep the manual numbering:

Step 1: Open the application
Step 2: Enter username / Enter password / Sign in
Expected 2: The Purchase Orders list is shown
Precondition: the order to approve is Draft
Step 3: Click Approve on that order's row
Expected 3: Its status is Approved
Precondition: the order to delete is listed
Step 4: Click Delete on the other order's row
Expected 4: That order is no longer in the list
Expected 5: The approved order is still listed as Approved

Notice: the run report maps straight back to TC-2001. Every negative check (“is no longer in the list”) has a positive check before it (“the order to delete is listed”). Without that, a wrong target would make the negative check pass for the wrong reason.


Option B. The master prompt plus the data-driven variant:

## The test to write
Application / screen: Fixture ERP - Sign in
Requirement: only user "tester" with password "correct horse" can sign in.
Any other combination shows the alert "Invalid credentials" and stays on Sign in.
Screen: <label>Username</label><input> <label>Password</label><input type=password>
<button data-testid="login">Sign In</button> <p role="alert">Invalid credentials</p>
After sign-in: heading "Purchase Orders".
Run once per row (inline data, independent rows, password column sensitive).
Columns: case, username, password, should_pass (yes/no).
Rows: the valid user; wrong password; unknown user; password in different case;
empty password; username with surrounding spaces.
Use control.if on data.should_pass to check success or the error.

Result: one set of steps, six rows, and an If / Else on the outcome:

"data": {
"source": {
"kind": "inline",
"columns": ["case", "username", "password", "should_pass"],
"rows": [
{ "case": "valid user", "username": "tester", "password": "correct horse", "should_pass": "yes" },
{ "case": "wrong password", "username": "tester", "password": "wrong", "should_pass": "no" },
...
]
},
"independent": true,
"sensitiveColumns": ["password"]
},
...
{
"id": "outcome", "type": "control.if", "label": "Check the outcome the row expects",
"params": { "condition": "data.should_pass == 'yes'" },
"children": [ /* the Purchase Orders list is shown */ ],
"orElse": [ /* the alert says Invalid credentials; the list is not shown */ ]
}

Notice: the case column names each row in the run report. These passwords sit in the data table only because they are deliberately fake; real credentials always stay in secrets. See Data-driven testing.


Option B:

## The test to write
Open {{baseUrl}}#slow. The "Processing" heading appears with the text "Loading...".
After a variable time (up to ~10 s), "Loading..." disappears and "Completed" is shown.
Check Completed is shown. Do NOT use a fixed wait: wait for "Loading..." to be hidden,
with a step timeout of 15000 ms.

Result:

Step Type Detail
Open the processing panel Open {{baseUrl}}#slow
The Processing heading is shown Visible heading Processing
Wait until Loading… disappears Wait for element text Loading…, state hidden, timeout 15000 ms
Completed is shown Visible text Completed

Notice: naming the signal to wait for (“Loading…” disappears) is what lets the AI choose Wait for element instead of Wait. The test waits as long as the page needs and no longer.


Option B. The master prompt plus the API variant:

## The test to write
API base: {{apiUrl}}. Secrets: API_USER, API_PASSWORD.
Auth: POST {{apiUrl}}/auth/token {"username","password"} -> 200 {"token","expiresIn"}.
Send "Authorization: Bearer <token>" on every other call.
Endpoints:
POST /vendors {"name","email"} -> 201 {"id":"V-1001","name","email","status":"Active"}
POST /orders {"vendorId","lines":[{"item","qty"}]} -> 201
{"id":"PO-1002","vendorId","lines":[{"item","qty","unitPrice","amount"}],"total","status":"Draft"}
422 {"error":"qty must be a positive whole number"} when qty <= 0 or not whole
GET /orders/{id} -> 200; 401 without a token
DELETE /orders/{id}, DELETE /vendors/{id} -> 204
Catalog prices: 1234 = 24.50, 5678 = 89.00
Cover: create a vendor with a unique name; create an order with 1234 x 2 and 5678 x 1;
check id prefix, status Draft, vendor, 2 lines and total 138; response under 800 ms
(soft); quantity 0 is rejected with the exact error; reading without a token gives 401.
Cleanup: delete the order and the vendor, accept 204 or 404, continue on error.

Result: 11 steps and 2 cleanup steps.

Step Type Detail
Give this run’s vendor a unique name Set variable CONCAT('AI Vendor ', NOW())
Sign in to the API HTTP request (sensitive) POST /auth/token, expect 200, saved as auth
Create the vendor the order needs HTTP request POST /vendors, expect 201
Create an order with two lines HTTP request POST /orders, expect 201, saved as order
The order is a Draft PO for this vendor Expression STARTS_WITH(order.body.id, 'PO-') AND order.body.status == 'Draft' AND …
The order has 2 lines Expression COUNT(order.body.lines) == 2
The total is 2 x 24.50 + 1 x 89.00 = 138.00 Expression order.body.total == 138
Creating the order took under 800 ms Expression (soft) order.durationMs < 800
Try an order line with quantity 0 HTTP request expect 422
The error explains the quantity rule Expression badQty.body.error == 'qty must be a positive whole number'
Reading the order without a token is refused HTTP request GET, expect 401
Cleanup: Delete the order / Delete the vendor HTTP request expect 204,404, on error Continue

Notice: the expected total is worked out in the prompt (2 × 24.50 + 89.00 = 138), so the AI asserts a number instead of “a total exists”. Cleanup runs even when a check fails. See API testing and Error handling.


The first drafts of these tests had four mistakes, and they are typical of what an AI writes. All four were caught by atx validate or by the first run, which is why you should always do both before saving a test:

Mistake in the draft Caught by Fix
A Text check with an empty expected value atx validate: expected must contain at least 1 character Extract the value, then Expression LENGTH(x) == 0
{{line.qty}} inside a target strategy’s value Run: Tried text=“{{line.qty}}” -> 0 Strategy values are literal text. Templates work in step parameters and in the row scope’s key.
A “no longer exists” check passed with a wrong target Review: the target never matched anything Check the element exists first, then that it is gone
css: td inside a row matched 4 cells Run: css=td -> 4 A target must match exactly one element: td:first-child, td.status

The guide’s master prompt now tells the AI each of these rules, so you should not meet them. They are still worth knowing when you review AI-written tests.


The seven test files are in the repository under examples/ai-prompts/:

# File Environment
1 01-login-wrong-password.json examples/fixture.env.json
2 02-save-without-lines.json examples/fixture.env.json
3 03-create-po-with-lines.json examples/fixture.env.json
4 04-manual-case-row-actions.json examples/fixture.env.json
5 05-login-data-driven.json examples/fixture.env.json
6 06-wait-for-processing.json examples/fixture.env.json
7 07-api-create-order.json examples/api-demo/environment.json, after npm run demo:api

Run one from the command line:

Terminal window
npx atx run examples/ai-prompts/03-create-po-with-lines.json --env examples/fixture.env.json --headed

examples/fixture.env.json holds an absolute file:// path to the fixture application; change its baseUrl to match where the repository is on your machine. To work with an example in the editor, import it (see Importing a test file) and create an environment with the same baseUrl and secrets.