# Assertion Results and Custom Checks

> Read assertion results row by row (expected, actual, passed), and write custom checks whose script log appears right under the result when they fail.

Source: https://totalshiftleft.ai/help-center/product-documentation/assertions-results-and-custom-checks

> **Applies to:** All editions · Web app and Desktop app · Any role that can run tests. Asking the assistant to build a check needs the Project Assistant and an AI provider.

## Overview

When a test runs, each of its assertions produces a row in the results: what was expected, what was found, and whether it passed. Shift-Left Studio makes those rows easier to read and act on:

- Every result row is **numbered** to match the assertion in the editor, and custom checks show their **name**, so you can tell nine custom checks apart.
- A custom check's **Expected** and **Actual** cells say what the check was looking for and what it found, instead of "custom logic to pass / passed".
- A custom check can write **log lines** that appear under its result row.
- You can **copy** what any results tab shows as plain text.
- You can **state a check in words** to the Project Assistant and it builds it for you.
- An assertion's expected value can be a **dynamic token**, for example a value from a data row.

## Key concepts

| Term | What it means |
|---|---|
| Result row | One outcome of one assertion in a run. An assertion can produce more than one row. |
| `#N` badge | The number of the assertion in the editor that produced the row. |
| **Custom Assertion** | An assertion whose logic is a short JavaScript script you write. |
| **Custom Assertion (Request)** | The same, but it checks the request that was sent. |
| Script log | Lines a custom check writes with `log(...)`, shown under its result row and in the run log. |
| Dynamic token | A placeholder such as `{{row.EXPECTED_STATUS}}` in an expected value, filled in when the test runs. |

## Before you begin

- Open a test in the Test Workbench (edit, run and see results on one screen).
- To ask the assistant to build a check, the Project Assistant must be available in your workspace and an AI provider configured under **Settings → AI Settings**.

## Step 1 — Find the row for a specific assertion

1. Run the test.
2. In the results pane, open the **Assertions** tab.
3. Each row carries a `#N` badge. Hover it to see *Assertion N in the editor*. That is the card with the same number on the editor's **Assertions** tab.
4. A custom check's row reads **Custom Assertion — <name>**, where the name is the check's own name, for example the `pm.test` name from an imported Postman collection. A field check shows its path, for example **Field Contains — data.message**.

> **Note:** For runs recorded before numbering existed, Studio matches rows to assertions by position only when the assertion type at that position agrees. A row it can't match safely shows no number rather than a wrong one.

## Step 2 — Read Expected and Actual for a custom check

For built-in assertions, Expected and Actual are the values compared, for example Expected `201`, Actual `200`. For a custom check, Studio fills the two cells from what the run actually produced, in this order:

| Source | Example Expected | Example Actual |
|---|---|---|
| The script returned `expected` and `actual` | `status 200 and 3 items` | `status 200 and 2 items` |
| The check is a converted Postman script with several `pm.test` checks | `all 8 checks to pass` | `6 of 8 passed — failed: …` (naming the failed checks) |
| The check has a name, and the script gave a reason | the check's name | the script's reason |
| Nothing more specific is available | `custom logic to pass` | `passed` or `failed` |

When a check passes and nothing more specific is known, Actual repeats Expected, the same way `Expected 200 / Actual 200` reads for a status check. Studio never guesses Expected and Actual by reading your script's text, because a confident guess that is sometimes wrong is worse than a plain placeholder.

If the script throws an error, Actual reads `error: <message>`, so you can tell a broken script apart from a failed check.

## Step 3 — Write a custom check that explains itself

1. On the editor's **Assertions** tab, add an assertion and choose **Custom Assertion**.
2. Write the logic. Your script can use these names: `response`, `request`, `responseTime`, `assertion`, `utils` and `log`. The response body is `response.data` and the status code is `response.status`.
3. Return either `true`/`false`, or an object with `passed` and, optionally, `reason`, `expected` and `actual`.
4. Call `log(...)` for anything that helps you read a failure later.

Example:

```js
const items = response.data.items || [];
log('items returned:', items.length);
return {
  passed: response.status === 200 && items.length === 3,
  expected: 'status 200 and 3 items',
  actual: `status ${response.status} and ${items.length} items`,
};
```

Log lines appear in a **Script log** block under the result row, and in the **Logs** tab prefixed `[SCRIPT]`. Studio keeps up to 50 lines per check, each up to 300 characters.

> **Tip:** Returning `expected` and `actual` is the most useful thing a custom check can do. It turns a wall of identical "passed" rows into rows that say what each one checked.

## Step 4 — Copy a results tab

1. In the results pane, open the tab you want: **Assertions**, **Response**, **Request**, **Variables** or **Logs**.
2. Click **Copy**. The button changes to **Copied** (or **Copy failed** if your browser blocked the clipboard).

Studio copies what the tab shows, as readable text, not the raw run record. On the **Assertions** tab, for example, each row is copied as its number, name, verdict, expected and actual value. This is handy for pasting into a bug report or a chat message.

## Step 5 — Ask the assistant to build a check you describe

You can describe a check in plain words and let the Project Assistant build it.

1. Open the Project Assistant from the test, or from the floating launcher.
2. Say what the check should do, for example *"Change assertion 6 so it checks the body is an object"* or *"Add a check that `data.total` equals the number of items."*
3. The assistant offers a change card showing exactly which assertion it adds or replaces.
4. Review the card and accept it. Nothing is changed until you accept.

How the assistant builds it:

- It uses a built-in assertion type when one fits. A check on the body's shape, for example, becomes **Response Is Object**, with no AI-written code.
- Only when no built-in type fits does it write a custom script, and that script must compile and must call `log(...)` so its result can be read.
- It refers to assertions by the numbers you see in the editor and in the results.
- If the test's last run was analyzed, the card shows that verdict as a note. When you state the check you want, the assistant builds it rather than refusing because of an earlier verdict.

> **Note:** Like every change the assistant offers, the new assertion goes through the same checks as any other repair. For example, it won't weaken a negative test into one that can never fail.

## Step 6 — Use dynamic tokens in expected values

An assertion's expected value can contain a token that is filled in at run time. In the value field, type `{{` to pick one.

- In a data-driven test, `{{row.COLUMN}}` uses the value from the current data row, for example `{{row.EXPECTED_STATUS}}` for the status each row should get back.
- A value saved by an earlier test's data extraction can be used by name, for example `{{orderId}}`.

Tokens are resolved only in the expected value. The field path or header name a check reads from is never treated as a template.

Status-code and other number-type assertions still accept tokens: the value field is a text box so you can type `{{row.EXPECTED_STATUS}}`.

**Generating checks from test-data sheets.** When you upload a test-data sheet, columns named `EXPECTED_STATUS` (or `EXPECTED_STATUSCODE`, `EXPECTED_HTTPSTATUS`, `EXPECTED_CODE`, `EXPECTED_RESPONSECODE`) and `EXPECTED_<path>`, such as `EXPECTED_data.id`, are read as expectations, not inputs. Data-driven tests generated from the sheet check each row's own expected status and fields. That's how one sheet of invalid inputs can mix rows that expect 400 and rows that expect 422.

## Understanding the results

| What you see | What it means |
|---|---|
| Green row | The assertion passed. |
| Red row | The assertion failed. Compare Expected and Actual. |
| Actual reads `error: …` | A custom script threw an error. Fix the script, not the API. |
| **Needs review** on an imported check | A Postman check that hasn't been converted. It reports a failure until converted. See [Postman collection import](/help-center/product-documentation/postman-collection-import). |
| Row with no `#N` badge | An older run whose row couldn't be matched to an assertion safely. |

## Troubleshooting

| Symptom | Why it happens | What to do |
|---|---|---|
| All custom rows read "custom logic to pass / passed" | The scripts return only `true`/`false` and the checks have no names. | Return `expected` and `actual` from each script. |
| A check with `.not` fails although the value "looks right" | In chai, `.not` negates every assertion word after it, including after `.and`. `.to.not.be.null.and.to.be.an('object')` fails on an object. | Rewrite as separate checks, for example `.to.not.be.null` and `.to.be.an('object')`. |
| **Copy failed** | The browser blocked clipboard access. | Allow clipboard access for the site, or select the text manually. |
| No Script log block appears | The script never called `log(...)`, or it threw before reaching it. | Add `log(...)` calls early in the script. |
| A token appears literally in Actual | The token name doesn't match a column or variable. | Check the spelling against the data set columns or the extraction's variable name. |

## Best practices

- Give each custom check a clear name, and return `expected` and `actual`.
- Prefer built-in assertion types. They explain themselves and run faster than scripts.
- Log the values a check depends on, not the whole response.
- When you report a failing assertion, copy the **Assertions** tab so the number and values travel with it.

## Related articles

- [Test cases: open, edit and save](/help-center/product-documentation/test-case)
- [Postman collection import](/help-center/product-documentation/postman-collection-import)
- [File uploads and form bodies](/help-center/product-documentation/file-uploads-and-form-bodies)
- [Project Assistant actions](/help-center/product-documentation/project-assistant-actions)

