In k6, how do you call check() from the k6 module, and what does each argument do?
answer
- three arguments, the last one optional
- object keys become the check names
- the value is passed to each predicate
- check(val, sets, [tags]) returns a boolean
basics
~20 sk6's check(), from the k6 module, has the signature check(val, sets, [tags]): a value, an object whose keys are check names and whose values are conditions called with that value, and optional extra tags. It returns a boolean.
solid answer
~40 sIn k6 v2 you import it by name — `import { check } from 'k6'` — and call `check(val, sets, [tags])` inside a VU function. `val` is the value under test, typically an HTTP response. `sets` is an object whose **keys become the check names** and whose values are the conditions; when a value is a function, k6 calls it with `val` as its only argument. The optional third argument is a flat object of extra tags. The call returns `true` only when every entry evaluated truthy. Two contract rules bite early: `check()` needs VU state, so calling it in the init context errors, and a check name may not contain `::`, which k6 reserves as its group-path separator.
code
javascript · 17 linesimport http from 'k6/http';
import { check } from 'k6';
export default function () {
const res = http.post(
'https://example.com/login',
JSON.stringify({ user: 'alice', password: 'secret' }),
{ headers: { 'Content-Type': 'application/json' } }
);
const ok = check(res, {
'login status is 200': (r) => r.status === 200,
'login body has a token': (r) => r.json('token') !== null,
});
console.log(ok); // true only if both entries were truthy
}go deeper
Memorise the shape: import { check } from 'k6', then check(value, { 'name': (v) => condition }). The key is the name shown in the results and the function receives the value you passed first.
Be able to explain that the second argument's values need not be functions, that a function is called with the tested value and this undefined, and that the call returns a plain boolean rather than throwing.
Show that you name each condition separately so a failure identifies itself, keep check() out of the init context, and treat the returned boolean as data your script decides what to do with.
Frame check naming as a contract: names travel into dashboards and alerts, so a team standard on wording and granularity is worth more than any individual assertion style debate.
## What `check()` is and where it comes from k6 ships a small built-in module named `k6`. In k6 v2 that module exports exactly five names — `check`, `fail`, `group`, `randomSeed` and `sleep` — and `check` is the only condition-testing primitive the binary contains. You reach it with a named import: ```javascript import { check } from 'k6'; ``` There is no global `check`, so the import is mandatory. There is also no `expect()` anywhere in the k6 binary; that name belongs to a separately imported library, not to the runtime. ## The signature: `check(val, sets, [tags])` | argument | required | what it holds | |---|---|---| | `val` | yes | the value under test — commonly an HTTP response object, but any JavaScript value is accepted | | `sets` | yes | an object whose **keys are the check names** and whose **values are the conditions** | | `tags` | no | a flat `{key: value}` object of extra tags attached to the results this one call produces | The call returns a **boolean**: `true` when every entry in `sets` evaluated truthy, `false` otherwise. It is an ordinary value you can assign, log or branch on. ## How k6 reads the `sets` object k6 walks the own keys of `sets` in order, and for each key: 1. **The key becomes the check's name**, character for character. That name is the check's identity in the run's results, so write it as a statement about the system — `'login status is 200'`, never `'check1'`. 2. **If the value is a function**, k6 calls it with exactly one argument — the `val` you passed first — and uses what it returns. The function is invoked with `this` undefined, so a non-arrow function cannot rely on `this`. 3. **If the value is not a function**, k6 uses it as it stands. `check(res, { 'ok': res.status === 200 })` is legal; JavaScript simply evaluated that expression while building the object literal, before `check()` was entered. 4. **The result is coerced with ordinary JavaScript truthiness** and recorded as a pass or a fail under that name. Because the value under test is handed to the predicate, these two styles behave identically: - `check(res, { 'status is 200': (r) => r.status === 200 })` — the parameter `r` **is** `res`. - `check(res, { 'status is 200': () => res.status === 200 })` — the closure reaches `res` directly. The predicate is plain JavaScript executed synchronously in the same VU runtime as the rest of the iteration. There is no sandbox and no separate context: whatever the surrounding script can see, the predicate can see. ## The worked case: validating a login response ```javascript import http from 'k6/http'; import { check } from 'k6'; export default function () { const res = http.post('https://example.com/login', JSON.stringify({ user: 'alice' }), { headers: { 'Content-Type': 'application/json' }, }); const ok = check(res, { 'login status is 200': (r) => r.status === 200, 'login body has a token': (r) => r.json('token') !== null, }); if (!ok) { // your own handling; check() itself stopped nothing } } ``` One value, two named conditions, one boolean back. Naming both conditions separately rather than combining them with `&&` is what makes the results readable: a single entry that ANDs status and body can only tell you that something was wrong, never which half. ## Two contract rules that bite immediately - **`check()` requires VU state.** Calling it at module scope — the init context, where imports run and `options` is defined — fails with *"Using check() in the init context is not supported"*. Conditions belong inside the exported default function or another `exec` target. - **A check name may not contain `::`.** k6 reserves `::` as the separator inside group paths, so any name containing it is rejected outright with *"group and check names may not contain '::'"*. Two smaller consequences of `sets` being an ordinary JavaScript object are worth knowing. A repeated key silently overwrites the earlier one, so two entries in one call can never share a name. And calling `check()` with no second argument at all fails with *"no checks provided to `check`"* rather than passing vacuously. The same object-ness has a stranger consequence: an **array** is a valid `sets` argument, because an array's own keys are its indices. `check(res, [(r) => r.status === 200])` runs happily and records a check named `0`. It is legal and almost never what you want — the name is the only human-readable thing a check result carries, and an index is not one.
- What happens if you call k6's check() in the init context?It fails with *"Using check() in the init context is not supported"*. `check()` needs the VU state that only exists once a VU is running an iteration, so it can only be called from the default function or another `exec` target — not at module scope where imports and `options` live.
- Can a k6 check name contain `::`?No. k6 uses `::` as the separator inside group paths, so a name containing it is rejected with *"group and check names may not contain '::'"* and the whole `check()` call fails. Every other character — spaces, punctuation, non-ASCII — is fine.
- Does the predicate have to be a function in a k6 check() set?No. A non-function value is used as it stands, so `check(res, { 'ok': res.status === 200 })` works. The difference is timing: that expression is evaluated by JavaScript while the object literal is built, before `check()` is called, rather than by `check()` itself.
saying these in an interview costs you the question
- Thinks a failed k6 check throws like a traditional assert
- Believes the predicate must be an arrow function
- Says the third argument tags the HTTP request itself
- Calls check() at module scope, in the init context
- Uses one entry ANDing every condition, losing which half failed