Docs Test Case Steps

Test Case Steps — Low-Code Flow

Mockarty test cases are built from steps. A step is one click in your test:
“open the login page”, “POST /auth”, “verify the welcome email arrives”.
This page is the practical guide to building steps the low-code way — pointing
and clicking instead of writing code — so a non-developer can author an
end-to-end test that calls real APIs, chains values from one call to the next,
debugs itself step by step, and lights up green or red on every run.

About URLs in examples: all examples use localhost:5770 as the default
Mockarty address. If your instance runs on a remote server, replace
localhost:5770 with its actual address (e.g. https://mockarty.company.com).
See Tips & Useful Features for details.

Related pages: Test Case Management ·
Runtime Flow View ·
Test Plans ·
Test Plan Run View ·
Override Surfaces ·
Secrets Storage ·
JsonPath Guide

To run a case inside a Test Plan and watch it expand inline alongside
other plan items (load, fuzz, chaos, contract, nested plans), see the
Test Plan Run View guide. For how
{{plan.X}} values flow between plan-level overrides and case-step
extracts, see Override Surfaces.

1. What a step is — manual or automatic

Every step in a test case is either manual or automatic. The choice is
a small two-icon switch at the top of each step row in the builder.

Mode Use when What runs at execution time
Manual (hand icon) A tester needs to look at the screen and decide pass/fail — UI inspection, visual diff, real-device checks, on-call escalation drills. Nothing automatic. The runner pauses, the step execution modal opens, the tester records the verdict.
Automatic (robot icon) The check can be expressed as an HTTP call (or chain of calls) whose response decides pass/fail. The runner makes the request, evaluates expectations, and records the result without any human in the loop.

You can mix both in the same case. A common pattern is automatic for
plumbing, manual for the user-visible result
: log in via API, fetch a user
profile via API, then ask a human “open the email client and confirm the
welcome message landed”.

To switch a step, click the hand or robot icon in the step header. The body
of the step morphs to show the right inputs for the mode you picked.

[Step row with mode switch — screenshot pending]

2. Linking an API Tester request to a step

For automatic steps, the work happens inside the API Tester — Mockarty’s
HTTP / gRPC / GraphQL client. You build and save a request there, then link
it from the step. The runner re-fetches the linked request on every run, so
edits in API Tester apply to existing cases instantly — no copy-paste, no
snapshot drift.

To link a request:

  1. Open the test case in the builder (/ui/test-cases).
  2. Make sure the step is in Automated mode (robot icon active).
  3. Inside the step body, click Pick endpoint.
  4. A tree picker opens, showing your saved API Tester requests grouped by
    collection. Type to filter by name, method, or path. Pick the one you want.
  5. The step body collapses to a single chip: POST /api/v1/auth — Login. The
    chip name follows the request name in API Tester — rename there and the
    chip follows.

[Linking an API Tester request to a step — screenshot pending]

To unlink, click the request chip and choose Clear. The step body returns
to the empty (none — pick a request) state.

What a linked request brings with it

When the case runs the linked step, Mockarty pulls the request definition
fresh from API Tester (method, URL, headers, body, expectations) and fires it
through the same engine that powers API Tester itself. The response is
captured against the step run so the runtime flow view
can show you exactly what came back.

3. Binding an environment to a step

API Tester organises hosts, base paths, tokens and per-team values into
environments (think “staging”, “production”, “regional-eu”). A step can
pick its own environment so the same logical test can target different
deployments without rewriting requests.

A step’s environment binding has three layers:

  1. Environment — one named environment from API Tester. Provides every
    {{env.X}} placeholder its variables.
  2. Overrides — a per-step list of key/value pairs that take precedence
    over the environment for this step only. Useful for “use staging for the
    whole case, but for this one step talk to staging-eu”.
  3. Secrets — pointers into the Secrets Storage
    with a per-step alias. The secret value is fetched and substituted at
    execution time; it is never written into the case definition.

To set the environment on a step:

  1. In the automatic step body, click Environment.
  2. Pick an environment from the dropdown (search by name). Leave blank to
    inherit the case-level default.
  3. Click Add override to add key = value rows. Each row provides one
    {{env.<key>}} value that wins over the environment’s own definition for
    this step.
  4. Click Add secret to bind a secret. The secret picker lists every
    secret the current user can read in this namespace. Choose the source
    secret and an alias — the alias is the name you use in the step
    ({{secret.<alias>}}), so you can reference {{secret.API_TOKEN}}
    regardless of whether the underlying secret is named prod_api_token or
    staging_api_token.

Realistic example: staging-eu login that uses a prod secret

A login step that hits the staging environment, but talks to the European
region by overriding host, and pulls its API token from the production
secrets store:

Layer Value
Environment Staging (provides host=https://api.staging.example.com, tenant=acme)
Override host = https://api.staging-eu.example.com
Secret source prod_api_token, alias API_TOKEN

The linked API Tester request URL is:

{{env.host}}/v1/auth

with header Authorization: Bearer {{secret.API_TOKEN}}. At execution time
the runner resolves the URL to https://api.staging-eu.example.com/v1/auth
(the override wins over the environment) and substitutes the token from the
prod secret store. The secret value is masked in the runtime view and in the
audit log — it is never visible after the run finishes.

4. Variable references inside a step

Anywhere a step accepts text — URL, header value, body, JSON path,
expectation — you can drop in a placeholder of the form {{namespace.key}}.
There are four namespaces:

Namespace Source Filled when
{{plan.X}} Values harvested by earlier steps in the same run (see §5). At the moment the earlier step’s extract rule fires.
{{override.X}} (implicit) The step’s per-step overrides list. When the step’s bindings are resolved at run start.
{{env.X}} The bound environment’s variables. When the step’s bindings are resolved at run start.
{{secret.X}} The Secrets Storage entry bound to the alias X. At the moment the request is built — never persisted.

Precedence — most specific wins

When the same name lives in multiple namespaces, Mockarty picks the most
specific value. Reading left to right, that order is:

plan  >  override  >  env  >  secret

So {{env.host}} for the step in §3 resolves to the override
https://api.staging-eu.example.com (the override beats the environment),
and a later step that extracted host into plan.host would beat the
override in turn.

The step row shows a small conflict badge next to any key that has
multiple sources active, and a tooltip lists which one will win at run time.
If the precedence is wrong for your case, the simplest fix is to rename one
of the keys (e.g. give the override a more specific name like host_eu).

5. Extracting data from one step for the next

A login step returns a token; the next step needs that token. Extracts
are the rules that copy values out of one step’s response and put them into
the run’s plan context ({{plan.X}}) so the next step can use them.

Open the step’s Extract section to add a rule. There are four kinds:

Kind What it pulls Example
JSONPath A value from the JSON response body. $.access_token
Regex A capture group from a header, the raw header block, or the body. Bearer\s+(?P<t>[A-Za-z0-9._-]+) against header.authorization
Header A single response header. X-Request-Id
Status The HTTP status code as an integer. (no input — just pick this kind)

Each rule has a To field — the name under plan. that downstream steps
will use to read the value back. So a JSONPath rule with From $.access_token
and To plan.token means “after this step passes, {{plan.token}}
contains the response body’s access token”.

For regex rules, the Source dropdown picks what to run the pattern
against:

  • body (default) — the raw response body.
  • header. — one header’s value (e.g. header.authorization).
  • header_raw — every response header concatenated as Name: value lines.

The named capture group t (or, if you don’t name it, capture group 1)
becomes the harvested value.

Realistic chain: login → fetch profile

Step Method Extract Next step uses
1. Login POST /auth JSONPath $.access_token → plan.token —
2. Fetch profile GET /me with Authorization: Bearer {{plan.token}} JSONPath $.id → plan.userId —
3. Audit log query GET /audit?user={{plan.userId}} — —

When step 1 passes, plan.token is written. Step 2 starts with that value
already substituted into the Authorization header. Step 2 in turn writes
plan.userId, and step 3 uses it in the query string.

Test on last response

The extract editor has a Test on last response button. After you’ve run
the step at least once in a debug run (see §8), this button replays your
extract rule against the cached response without touching the server, so
you can iterate on the JSONPath / regex without re-firing the API. The
button shows the resolved value next to the rule, or an error message if
the path doesn’t match.

If the cached response is on the server only — for example you ran the case
on a different machine — the button falls back to a tiny server-side
preview call that does the same evaluation and returns the value.

5a. Asserting on the response — structured pass/fail checks

Extracts harvest values for the next step; assertions decide whether
this step passed at all. A step that calls POST /auth and gets back
503 Service Unavailable is technically a “completed request” — without
an assertion, the dispatcher accepts it as a PASS and dutifully writes a
poisoned token into plan.token. Adding status == 200 to the step’s
assertions list closes that hole: the step fails closed, the harvester
is skipped, and downstream steps never see the bad data.

Open the step’s Assertions section to add a check. There are six kinds:

Kind What it checks Operators
status The HTTP status code = ≠ > ≥ < ≤
header A response header by name (case-insensitive) = ≠ contains not_contain regex_match exists not_exists
jsonpath A JSONPath leaf in the response body All of the above plus > ≥ < ≤ when the leaf is numeric
body_contains The raw body as a string contains not_contain regex_match
duration_ms The step’s wall-clock in milliseconds = ≠ > ≥ < ≤
body_size The response body byte count = ≠ > ≥ < ≤

Each assertion has:

  • Kind — picks what to check against.
  • Source — required for header (header name) and jsonpath (a path
    like $.user.id). The other four kinds don’t use it.
  • Operator — comparison op. Numeric ops work on numeric leaves; the
    string ops compare as strings; regex_match compiles Expected as a
    regular expression and tests the actual value against it.
  • Expected — the value to compare to. For regex_match it’s the
    pattern; for exists/not_exists it’s ignored.
  • Note — optional human-readable note shown next to the failure in
    the runtime view, so a teammate reviewing a failed run sees why the
    assertion was important, not just that it failed.

When to use which

You want to check that… Use
The API responded with a success code status = 200 (or >= 200 + < 300)
A specific user came back in the body jsonpath $.user.email = ${env.email}
The token shape looks like a JWT jsonpath $.token regex_match ^eyJ[A-Za-z0-9._-]+$
The trace id header was set header X-Request-Id exists
The response did NOT echo a debug payload body_contains not_contain DEBUG_TRACE
The endpoint stayed under the SLA budget duration_ms < 500
The body wasn’t suspiciously empty body_size > 0

Test on last response

The assertions editor mirrors the Extracts editor: a Test on last
response
button lets you paste a sample payload (or pick up the one
cached from a previous debug run) and runs every assertion in-browser.
Each row shows pass or the rendered failure message — useful when you’re
authoring a regex or refining a JSONPath.

What happens at runtime

Assertions run after the executor returns and before the
harvester writes into the plan context. The flow per step is:

  1. Executor sends the request, captures the response.
  2. Assertions evaluate against the response. ANY failure → step fails
    closed, with a multi-line error listing every failure.
  3. Harvest runs only when the step passes — so a failed assertion
    never produces a plan.X value.

The runtime view surfaces the failures inline: each step that failed on
assertions gets a red “Assertions” chip in its card header and an
“Assertions” section in the detail panel listing every failed check with
the kind, op, expected, actual, and your optional Note.

6. Step dependencies

By default, steps run in order: step 2 only starts after step 1 has
ended. That works for most cases but means a slow step blocks faster ones
even when there’s no real data dependency between them.

The Depends on picker — a dropdown inside the step body, listing every
other step in the current case — lets you make this explicit. The runner
treats dependsOn as the only real ordering constraint:

  • When you add a brand-new step, its dependsOn is pre-filled with the
    step immediately above (so the legacy “run sequentially” behaviour is
    preserved by default).
  • Remove the dependency to let the step run as soon as the case starts —
    in parallel with its siblings.
  • Add multiple dependencies to fan in: “this step needs both Login and
    Seed data to finish first”.
  • Branch from one step into many: “after Login is done, fetch profile,
    fetch orders, fetch settings — all three in parallel”.

The header of every step that has at least one dependency shows a small
link badge with the dependency count. Hover for the list of upstream
step names.

When a dependency ends in failed or cancelled, every downstream
step is marked skipped with the reason dependency failed: <step name>.
The runtime view colours these chips grey so it’s obvious which branch was
abandoned.

When to fan out

  • Independent setup work — seeding fixture data, warming caches,
    pre-creating test users — runs in parallel and shortens overall case
    time.
  • Independent assertions — three different endpoints all need to
    succeed after a deployment — run in parallel for the same reason.

When to keep things linear

  • Token-chain — step N needs {{plan.X}} written by step N-1. Keep the
    default dependsOn: [previous] and the runner serialises automatically.
  • Stateful operations — “create order” must finish before “cancel
    order”. Express the order with dependsOn.

7. Watching a run live

Click Run on a case, or run a Test Plan item that
references the case, and the Runtime Flow View opens.

Every step is a card in a tree. Expand a card to see, inline:

  • the request that was fired (URL, headers, body — fully resolved, with
    secrets masked);
  • the response (status, headers, body, duration);
  • the env summary chip — which environment, how many overrides, how
    many secrets were active;
  • every value the extract rules harvested (e.g. plan.token = "ey…").

A small chip on each step card carries the live status: pending,
waiting (gated on a dependency), running, passed, failed,
skipped, awaiting manual.

The runtime view updates as the run progresses — there is no need to
refresh. The full reference for the view is on the
Runtime Flow View page.

8. Sequential debug

A debug run runs the case the same way as a normal run except it pauses
after every step. You inspect what just happened, decide what to do next,
and click Continue to move on.

To start a debug run:

  1. Open the case.
  2. In the run menu, pick Debug run instead of plain Run.
  3. The first step starts, finishes, and the runtime view shows a yellow
    paused bar at the bottom with three buttons:
Button What it does
Continue Releases the pause. The next step (or next layer of parallel steps) starts.
Continue & edit Lets you tweak the harvested plan values, the step’s overrides, or the next step’s request before resuming. The change is recorded in the run’s history.
Stop Cancels the run. Already-completed steps keep their results; the case run ends in cancelled.

Use Continue & edit to fix a flaky token mid-debug: edit
plan.token by hand to a known-good value, click Resume, see the next
step succeed.

Debug runs are the same kind of run as normal runs — they show up in the
case’s history, count against retention quotas, and feed the same Allure
report when bundled into a Test Plan. The only difference is the per-step
pause.

9. Common pitfalls

The runner is opinionated about half-finished configuration. When a step
can’t run, the runtime view marks it red and the error chip in the step
header is the verbatim message — copy it back here to find the fix.

“unresolved placeholder: {{plan.X}}”

The step’s text contains {{plan.X}} but no earlier step wrote a value
under that name.

Fix: open the upstream step (the one that should produce X), add an
extract rule with To plan.X. If you renamed it, fix the downstream
references too — the conflict badge in the step row catches most of them.

“secret not found: <alias>”

The step references {{secret.<alias>}} but no secret is bound to that
alias on the step.

Fix: open the step’s environment binding, click Add secret, pick
the source secret from the picker, and set the alias to <alias>. If the
secret picker is empty, you don’t have read access to any secret in this
namespace — ask a namespace admin to grant it, or use the
Secrets Storage page to create one.

“dependency cycle detected”

You set dependsOn on two or more steps such that following the arrows
brings you back where you started (A → B → A).

Fix: open the Depends on picker on the steps in the cycle and remove
one of the back-edges. The runtime view lists the offending step names in
the error message — start there.

“conflict — env override beats plan value”

The same key is being written by both an upstream step’s extract (into
plan.X) and the current step’s overrides. The runtime view shows a
conflict badge to warn that the plan-context value won the race.

Fix: decide which source you actually want. If the upstream step’s
value should win, delete the override row on the downstream step. If the
override should win, rename either the override key or the upstream
extract’s To so the two never collide.

“environment not found”

The bound environment was deleted or moved between namespaces after the
step was authored. The step run fails immediately on resolve.

Fix: open the step’s environment dropdown and re-pick. If the
environment really did go away, recreate it in API Tester or pick a
different one with the same variable names.


Continue on failure

By default, when ANY step in a test case fails, the runner cascades the
failure: every remaining step is skipped and the case ends with status
Failed. That matches how a smoke test usually behaves — the first
broken thing tells you the whole flow is broken.

But not every step is load-bearing. Maybe step 3 is “post a metric to
the analytics endpoint” — useful to record, not worth aborting the
release flow for. The Continue case if this step fails toggle on
each step row marks the step as non-blocking:

  • A Fail on a non-blocking step records the failure (counts toward
    the failed-step counter, lights the step row red in the runtime view)
    but the runner activates the next pending step normally.
  • The case ends with the dedicated status Passed (with soft failures)
    when every other step passed. That’s amber — distinct from green
    Passed and from red Failed so dashboards and CI gates can
    decide how strict they want to be.
  • If a later blocking step fails (or the user picks “stop the case”
    in the resolve modal — see below), the case still ends Failed and
    remaining steps cascade-skip — hard failures dominate amber.

Per-attempt override (manual resolve modal)

The step’s authoring flag is the default, but the user can override it
for a single resolution. In the manual-resolve modal the Continue on
fail
checkbox next to the Fail button is pre-populated from the step’s
authoring flag:

  • Leave it as-is to honour the case author’s intent.
  • Check it (when authoring says blocking) to continue the case despite
    this one failure — useful when you know the failure is environmental
    and not worth re-running.
  • Uncheck it (when authoring says non-blocking) to force the cascade —
    useful when you spot a deeper problem and want to stop the line.

The override is recorded against the attempt row alongside the
attachments and note, so the audit trail shows who explicitly continued
or stopped a case beyond what the author had configured.

When to use it

  • Use it on: telemetry posts, optional cleanup steps, “nice to
    have” assertions, follow-up calls that don’t gate the core flow.
  • Don’t use it on: authentication, the primary action under test,
    setup that downstream steps depend on (the case keeps going but
    downstream steps will fail too — and at that point you’ve lost the
    signal a hard fail would have given you).

Audit trail

The audit log captures every step resolution:

  • Step resolved — logged on every resolve, regardless of outcome.
  • Fail continued — logged specifically when the user explicitly picked “continue” on a failing step. This makes it clear when the cascade was suppressed by a human choice versus the step’s authoring default.

Admins can review this trail in Admin → Audit.


Next: read Runtime Flow View for the
full reference on the live execution view, and
Test Case Management for the broader case / folder /
version model.