|
| 1 | +--- |
| 2 | +title: 'Assertions' |
| 3 | +description: 'Advanced test assertions in k6 using the k6-testing library for both protocol and browser testing' |
| 4 | +weight: 04 |
| 5 | +--- |
| 6 | + |
| 7 | +{{< docs/shared source="k6" lookup="preview-feature.md" version="<K6_VERSION>" >}} |
| 8 | + |
| 9 | +# Assertions |
| 10 | + |
| 11 | +k6 provides test assertions in the form of the `expect` function. Assertions validate that your application behaves as expected during testing. |
| 12 | + |
| 13 | +Define assertions by passing a value to `expect()` and chaining it with a matcher that defines your expected outcome. The library provides expressive matchers that work with both protocol testing (HTTP/API) and browser testing scenarios. |
| 14 | + |
| 15 | +The assertions API is compatible with Playwright's assertion syntax, providing a fluent interface that improves test readability and reliability. |
| 16 | + |
| 17 | +## Getting started |
| 18 | + |
| 19 | +Assertions are provided by the [k6-testing library](https://jslib.k6.io). Import the library to start using assertions: |
| 20 | + |
| 21 | +```javascript |
| 22 | +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; |
| 23 | +import { browser } from "k6/browser"; |
| 24 | +import http from "k6/http"; |
| 25 | + |
| 26 | +export function protocolTest() { |
| 27 | + // Get the home page of k6's Quick Pizza app |
| 28 | + const response = http.get("https://quickpizza.grafana.com/"); |
| 29 | + |
| 30 | + // Simple assertions |
| 31 | + expect(response.status).toBe(200); |
| 32 | + expect(response.error).toEqual(""); |
| 33 | + expect(response.body).toBeDefined(); |
| 34 | +} |
| 35 | + |
| 36 | +export async function browserTest() { |
| 37 | + const page = await browser.newPage(); |
| 38 | + |
| 39 | + try { |
| 40 | + await page.goto("https://quickpizza.grafana.com/"); |
| 41 | + await page.waitForLoadState("networkidle"); // waits until the `networkidle` event |
| 42 | + |
| 43 | + // Assert the "Pizza Please" button is visible |
| 44 | + await expect(page.locator("button[name=pizza-please]")).toBeVisible(); |
| 45 | + } finally { |
| 46 | + await page.close(); |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +export const options = { |
| 51 | + scenarios: { |
| 52 | + // Protocol tests |
| 53 | + protocol: { |
| 54 | + executor: "shared-iterations", |
| 55 | + vus: 1, |
| 56 | + iterations: 1, |
| 57 | + exec: "protocolTest", |
| 58 | + }, |
| 59 | + |
| 60 | + // Browser tests |
| 61 | + ui: { |
| 62 | + executor: "shared-iterations", |
| 63 | + options: { |
| 64 | + browser: { |
| 65 | + type: "chromium", |
| 66 | + }, |
| 67 | + }, |
| 68 | + exec: "browserTest", |
| 69 | + }, |
| 70 | + }, |
| 71 | +}; |
| 72 | +``` |
| 73 | + |
| 74 | +## Types of assertions |
| 75 | + |
| 76 | +The k6-testing library provides two types of assertions, each designed for different testing scenarios: |
| 77 | + |
| 78 | +### Non-retrying assertions |
| 79 | + |
| 80 | +[Non-retrying assertions](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/testing/non-retrying-assertions) evaluate conditions immediately at a single point in time without retrying. They're ideal for validating static data such as HTTP response content, data structures, or configuration values. |
| 81 | + |
| 82 | +```javascript |
| 83 | +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; |
| 84 | +import http from 'k6/http'; |
| 85 | + |
| 86 | +export default function () { |
| 87 | + const pizzaRequestPayload = { maxCaloriesPerSlice: 1000, mustBeVegetarian: true }; |
| 88 | + const pizzaRequestHeader = { |
| 89 | + "Content-Type": "application/json", |
| 90 | + "Authorization": "Token " + "abcdef0123456789" |
| 91 | + } |
| 92 | + |
| 93 | + const response = http.post( |
| 94 | + `https://quickpizza.grafana.com/api/pizza`, |
| 95 | + JSON.stringify(pizzaRequestPayload), |
| 96 | + { headers: pizzaRequestHeader } |
| 97 | + ); |
| 98 | + const data = response.json(); |
| 99 | + |
| 100 | + // These assertions evaluate immediately |
| 101 | + expect(response.status).toEqual(200); |
| 102 | + expect(response.headers["Content-Type"]).toBeDefined(); |
| 103 | + expect(response.headers["Content-Type"]).toEqual("application/json"); |
| 104 | + expect(data.pizza).toBeDefined(); |
| 105 | + expect(data.pizza.name).toBeDefined(); |
| 106 | + expect(data.pizza.name).not.toHaveLength(0); |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +### Auto-retrying assertions |
| 111 | + |
| 112 | +[Auto-retrying assertions](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/testing/retrying-assertions) automatically retry until conditions become true or a timeout is reached. They're designed for browser testing scenarios where elements may take time to load, update, or become interactable. |
| 113 | + |
| 114 | +```javascript |
| 115 | +import { expect } from 'https://jslib.k6.io/k6-testing/0.5.0/index.js'; |
| 116 | +import { browser } from 'k6/browser'; |
| 117 | + |
| 118 | +export default async function() { |
| 119 | + // Open a new browser page |
| 120 | + const page = await browser.newPage() |
| 121 | + |
| 122 | + try { |
| 123 | + // Navigate to the quickpizza website |
| 124 | + await page.goto('https://quickpizza.grafana.com/') |
| 125 | + |
| 126 | + // Click the 'Pizza please' button |
| 127 | + await page.locator('button[name="pizza-please"]').click() |
| 128 | + |
| 129 | + // Take a screenshot of the homepage, and save it to the local filesystem |
| 130 | + // so we can inspect it later if needed. |
| 131 | + await page.screenshot({ path: 'homepage.png' }) |
| 132 | + |
| 133 | + // Check if the pizza recipe is displayed |
| 134 | + const textContent = await pizzaRecipeIsDisplayed(page) |
| 135 | + expect(textContent).toEqual('Our recommendation:') |
| 136 | + } finally { |
| 137 | + await page.close() |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +// Browsers are asynchronous, so we need to wait for the content we want to check |
| 142 | +// to be visible. |
| 143 | +async function pizzaRecipeIsDisplayed(page) { |
| 144 | + const label = await page.locator('h2[id="pizza-name"]') |
| 145 | + await label.isVisible() |
| 146 | + const textContent = (await label.textContent()).trim() |
| 147 | + |
| 148 | + return textContent |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +## Assertions vs. checks |
| 153 | + |
| 154 | +Unlike k6 [checks](https://grafana.com/docs/k6/<K6_VERSION>/using-k6/checks), which continue execution when they fail and register metrics for threshold evaluation, assertions immediately fail and interrupt the test when expectations are not met. Assertions are designed for validating critical conditions that determine whether a test should continue—when these conditions aren't met, they invalidate the entire testing scenario. |
| 155 | + |
| 156 | +Assertions do not register metrics because they halt execution rather than collect data points. This design provides fast feedback and detailed error messages to help you identify issues quickly, ensuring your tests only proceed when fundamental assumptions are satisfied. |
| 157 | + |
| 158 | +## Available assertion methods |
| 159 | + |
| 160 | +### Non-retrying assertions |
| 161 | + |
| 162 | +Use these for immediate evaluation of static values: |
| 163 | + |
| 164 | +| Method | Description | |
| 165 | +| --- | --- | |
| 166 | +| [toBe()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobe) | Exact equality using Object.is() | |
| 167 | +| [toEqual()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/toequal) | Deep equality comparison | |
| 168 | +| [toBeTruthy()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobetruthy) | Value is truthy | |
| 169 | +| [toBeFalsy()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobefalsy) | Value is falsy | |
| 170 | +| [toBeDefined()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobedefined) | Value is not undefined | |
| 171 | +| [toBeUndefined()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobeundefined) | Value is undefined | |
| 172 | +| [toBeNull()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobenull) | Value is null | |
| 173 | +| [toBeGreaterThan()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobegreaterthan) | Numeric greater than | |
| 174 | +| [toBeGreaterThanOrEqual()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobegreaterthanorequal) | Numeric greater than or equal | |
| 175 | +| [toBeLessThan()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobelessthan) | Numeric less than | |
| 176 | +| [toBeLessThanOrEqual()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobelessthanorequal) | Numeric less than or equal | |
| 177 | +| [toBeCloseTo()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobecloseto) | Floating point comparison | |
| 178 | +| [toContain()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tocontain) | Array/string contains value | |
| 179 | +| [toContainEqual()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tocontainequal) | Array contains object with matching content | |
| 180 | +| [toHaveLength()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tohavelength) | Array/string has specific length | |
| 181 | +| [toHaveProperty()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tohaveproperty) | Object has specific property | |
| 182 | +| [toBeInstanceOf()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/non-retrying-assertions/tobeinstanceof) | Value is instance of class | |
| 183 | + |
| 184 | +[See all non-retrying assertions →](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/testing/non-retrying-assertions) |
| 185 | + |
| 186 | +### Auto-retrying assertions |
| 187 | + |
| 188 | +Essential for browser testing with dynamic content: |
| 189 | + |
| 190 | +| Method | Description | |
| 191 | +| --- | --- | |
| 192 | +| [toBeVisible()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tobevisible) | Element is visible on the page | |
| 193 | +| [toBeHidden()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tobehidden) | Element is hidden or not visible | |
| 194 | +| [toBeEnabled()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tobeenabled) | Element is enabled and interactive | |
| 195 | +| [toBeDisabled()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tobedisabled) | Element is disabled | |
| 196 | +| [toBeChecked()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tobechecked) | Checkbox or radio button is checked | |
| 197 | +| [toBeEditable()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tobeeditable) | Element is editable | |
| 198 | +| [toHaveText()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tohavetext) | Element has specific text content | |
| 199 | +| [toContainText()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tocontaintext) | Element contains specific text | |
| 200 | +| [toHaveValue()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tohavevalue) | Input element has specific value | |
| 201 | +| [toHaveAttribute()](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/k6-testing/retrying-assertions/tohaveattribute) | Element has specific attribute value | |
| 202 | + |
| 203 | +[See all retrying assertions →](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/testing/retrying-assertions) |
| 204 | + |
| 205 | +## Assertion features |
| 206 | + |
| 207 | +### Negation |
| 208 | + |
| 209 | +All assertions can be negated using `.not`: |
| 210 | + |
| 211 | +```javascript |
| 212 | +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; |
| 213 | +import http from 'k6/http'; |
| 214 | + |
| 215 | +export default function () { |
| 216 | + const response = http.get('https://quickpizza.grafana.com/'); |
| 217 | + |
| 218 | + // Negated assertions |
| 219 | + expect(response.status).not.toBe(404); |
| 220 | + expect(response.body).not.toHaveLength(0); |
| 221 | + expect(response.headers).not.toHaveProperty('error'); |
| 222 | + |
| 223 | + // Browser negation (with await) |
| 224 | + await expect(page.locator('.error-message')).not.toBeVisible(); |
| 225 | +} |
| 226 | +``` |
| 227 | + |
| 228 | +### Soft assertions |
| 229 | + |
| 230 | +Soft assertions continue test execution even when they fail, marking the test as failed but allowing subsequent assertions to run: |
| 231 | + |
| 232 | +```javascript |
| 233 | +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; |
| 234 | +import http from 'k6/http'; |
| 235 | + |
| 236 | +export default function () { |
| 237 | + const response = http.get('https://quickpizza.grafana.com/'); |
| 238 | + |
| 239 | + // These will all run even if some fail |
| 240 | + expect.soft(response.status).toBe(200); |
| 241 | + expect.soft(response.headers['Content-Type']).toContain('text/html'); |
| 242 | + expect.soft(response.body).toHaveLength(response.body.length); |
| 243 | + |
| 244 | + // Test continues and performs additional checks |
| 245 | + console.log('Test completed, checking results...'); |
| 246 | +} |
| 247 | +``` |
| 248 | + |
| 249 | +### Custom error messages |
| 250 | + |
| 251 | +Provide descriptive error messages for better debugging: |
| 252 | + |
| 253 | +```javascript |
| 254 | +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; |
| 255 | +import http from 'k6/http'; |
| 256 | + |
| 257 | +export default function () { |
| 258 | + const response = http.get('https://quickpizza.grafana.com/api/pizza', { |
| 259 | + headers: { 'Content-Type': 'application/json' } |
| 260 | + }); |
| 261 | + const pizza = response.json(); |
| 262 | + |
| 263 | + expect(response.status, 'API should return successful response').toBe(200); |
| 264 | + expect(pizza.name, 'Pizza should have a valid name').toBeDefined(); |
| 265 | + expect(pizza.name, 'Pizza name should not be empty').not.toHaveLength(0); |
| 266 | +} |
| 267 | +``` |
| 268 | + |
| 269 | +## Configuration and customization |
| 270 | + |
| 271 | +### Global configuration |
| 272 | + |
| 273 | +Configure assertion behavior globally for all tests: |
| 274 | + |
| 275 | +```javascript |
| 276 | +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; |
| 277 | +import http from 'k6/http'; |
| 278 | + |
| 279 | +// Configure global settings |
| 280 | +const configuredExpect = expect.configure({ |
| 281 | + timeout: 10000, // 10 seconds for retrying assertions |
| 282 | + interval: 500, // Check every 500ms |
| 283 | + colorize: true, // Enable colored output |
| 284 | + softMode: 'fail' // How soft assertions behave |
| 285 | +}); |
| 286 | + |
| 287 | +export default function () { |
| 288 | + const response = http.get("https://quickpizza.grafana.com"); |
| 289 | + |
| 290 | + // All assertions use these settings |
| 291 | + configuredExpect(response.status).toBe(200); |
| 292 | +} |
| 293 | +``` |
| 294 | + |
| 295 | +### Per-scenario configuration |
| 296 | + |
| 297 | +Create different configurations for different test scenarios: |
| 298 | + |
| 299 | +```javascript |
| 300 | +import { expect } from 'https://jslib.k6.io/k6-testing/{{< param "JSLIB_TESTING_VERSION" >}}/index.js'; |
| 301 | +import http from 'k6/http'; |
| 302 | +import { browser } from 'k6/browser'; |
| 303 | + |
| 304 | +// Fast assertions for API testing |
| 305 | +const fastExpect = expect.configure({ |
| 306 | + timeout: 2000, |
| 307 | + interval: 100 |
| 308 | +}); |
| 309 | + |
| 310 | +// Slow assertions for complex browser interactions |
| 311 | +const slowExpect = expect.configure({ |
| 312 | + timeout: 30000, |
| 313 | + interval: 1000, |
| 314 | + softMode: 'continue' |
| 315 | +}); |
| 316 | + |
| 317 | +export default async function () { |
| 318 | + // Use appropriate expectation based on test type |
| 319 | + const response = http.get('https://quickpizza.grafana.com/'); |
| 320 | + fastExpect(response.status).toBe(200); |
| 321 | + |
| 322 | + if (__ENV.BROWSER_TEST) { |
| 323 | + const page = await browser.newPage(); |
| 324 | + await page.goto('https://quickpizza.grafana.com/'); |
| 325 | + await slowExpect(page.locator('h1')).toBeVisible(); |
| 326 | + } |
| 327 | +} |
| 328 | +``` |
| 329 | + |
| 330 | +## Best practices |
| 331 | + |
| 332 | +For tests that primarily use assertions to validate system state, such as tests verifying a successful deployment in CI pipelines, run k6 tests with assertions in quiet mode: |
| 333 | + |
| 334 | +```bash |
| 335 | +k6 run --quiet --no-summary script.ts |
| 336 | +``` |
| 337 | + |
| 338 | +This approach eliminates unnecessary output and focuses on the assertion results, making it ideal for automated testing environments. |
| 339 | + |
| 340 | +## See also |
| 341 | + |
| 342 | +- [expect() API reference](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/testing/expect) - Complete assertion documentation |
| 343 | +- [Auto-retrying assertions](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/testing/retrying-assertions) |
| 344 | +- [Non-retrying assertions](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/jslib/testing/non-retrying-assertions) |
| 345 | +- [Browser testing](https://grafana.com/docs/k6/<K6_VERSION>/using-k6-browser) - Browser automation with k6 |
| 346 | +- [Checks](https://grafana.com/docs/k6/<K6_VERSION>/using-k6/checks) - Alternative validation approach |
0 commit comments