Checks validate boolean conditions in your k6 tests. They’re similar to assertions in other testing frameworks, but with one crucial difference: failed checks don’t stop the test.
Instead, k6 tracks the pass/fail rate of checks and continues executing the test.
How checks work
Checks validate that your system responds correctly. For example, you might check that:
A POST request returns status 201
A response body contains expected text
A response completes within a time limit
k6 tracks each check as a rate metric , recording the percentage of passing checks.
To make a failed check abort or fail the entire test, combine checks with Thresholds .
Check for HTTP response code
The most common check validates the HTTP status code:
import { check } from 'k6' ;
import http from 'k6/http' ;
export default function () {
const res = http . get ( 'http://test.k6.io/' );
check ( res , {
'is status 200' : ( r ) => r . status === 200 ,
});
}
The check function takes:
The value to check (usually the response object)
An object mapping check names to validation functions
Check for response body content
Sometimes an HTTP 200 response contains an error message. Validate the response body to catch these cases:
import { check } from 'k6' ;
import http from 'k6/http' ;
export default function () {
const res = http . get ( 'http://test.k6.io/' );
check ( res , {
'verify homepage text' : ( r ) =>
r . body . includes ( 'Collection of simple web-pages suitable for load testing' ),
});
}
Check for response body size
You can verify response body size to detect unexpected changes:
import { check } from 'k6' ;
import http from 'k6/http' ;
export default function () {
const res = http . get ( 'http://test.k6.io/' );
check ( res , {
'body size is 11,105 bytes' : ( r ) => r . body . length == 11105 ,
});
}
Multiple checks
You can define multiple checks in a single check() statement:
import { check } from 'k6' ;
import http from 'k6/http' ;
export default function () {
const res = http . get ( 'http://test.k6.io/' );
check ( res , {
'is status 200' : ( r ) => r . status === 200 ,
'body size is 11,105 bytes' : ( r ) => r . body . length == 11105 ,
'response time < 200ms' : ( r ) => r . timings . duration < 200 ,
});
}
When you run this test, output shows each check individually:
$ k6 run checks.js
...
✓ is status 200
✓ body size is 11,105 bytes
✓ response time < 200ms
...
checks.........................: 100.00% ✓ 3 ✗ 0
data_received..................: 11 kB 20 kB/s
Check results in output
When your script includes checks, the end-of-test summary displays check statistics:
$ k6 run script.js
...
✓ is status 200
...
checks.........................: 100.00% ✓ 1 ✗ 0
data_received..................: 11 kB 12 kB/s
This shows:
Individual check results (✓ for pass, ✗ for fail)
Overall pass rate (100.00%)
Total passing (✓ 1) and failing (✗ 0) checks
Common check patterns
Status codes
Response content
Response timing
Headers
check ( res , {
'is status 200' : ( r ) => r . status === 200 ,
'is status 2xx' : ( r ) => r . status >= 200 && r . status < 300 ,
'is not 404' : ( r ) => r . status !== 404 ,
});
Checks vs thresholds
Checks alone don’t fail tests. A test with failing checks still exits with code 0 (success).
If you need the test to fail based on check results, combine checks with thresholds:
import http from 'k6/http' ;
import { check } from 'k6' ;
export const options = {
vus: 50 ,
duration: '10s' ,
thresholds: {
// Fail the test if less than 90% of checks pass
checks: [ 'rate>0.9' ],
},
};
export default function () {
const res = http . get ( 'https://quickpizza.grafana.com/api/status/500' );
check ( res , {
'status is 500' : ( r ) => r . status == 500 ,
});
}
Now the test fails if the check success rate drops below 90%.
Use tags to create thresholds for specific checks:
import http from 'k6/http' ;
import { check } from 'k6' ;
export const options = {
vus: 50 ,
duration: '10s' ,
thresholds: {
'checks{myTag:critical}' : [ 'rate>0.99' ], // 99% pass rate for critical checks
'checks{myTag:optional}' : [ 'rate>0.8' ], // 80% pass rate for optional checks
},
};
export default function () {
const res = http . get ( 'https://quickpizza.grafana.com/' );
// Critical check with tag
check (
res ,
{
'status is 200' : ( r ) => r . status === 200 ,
},
{ myTag: 'critical' }
);
// Optional check with different tag
check (
res ,
{
'response time < 100ms' : ( r ) => r . timings . duration < 100 ,
},
{ myTag: 'optional' }
);
}
Complete example
Here’s a comprehensive example demonstrating various check patterns:
import http from 'k6/http' ;
import { check , sleep } from 'k6' ;
export const options = {
vus: 10 ,
duration: '30s' ,
thresholds: {
checks: [ 'rate>0.95' ], // 95% of checks must pass
},
};
const baseUrl = 'https://quickpizza.grafana.com' ;
export default function () {
// Test homepage
const homeRes = http . get ( baseUrl );
check ( homeRes , {
'homepage status is 200' : ( r ) => r . status === 200 ,
'homepage loads quickly' : ( r ) => r . timings . duration < 500 ,
});
// Test API endpoint
const apiRes = http . get ( ` ${ baseUrl } /api/pizzas` );
check ( apiRes , {
'api status is 200' : ( r ) => r . status === 200 ,
'api returns JSON' : ( r ) => r . headers [ 'Content-Type' ]. includes ( 'application/json' ),
'api has pizzas' : ( r ) => {
const body = r . json ();
return Array . isArray ( body ) && body . length > 0 ;
},
});
// Test error handling
const errorRes = http . get ( ` ${ baseUrl } /api/not-found` );
check ( errorRes , {
'not found returns 404' : ( r ) => r . status === 404 ,
});
sleep ( 1 );
}
Best practices
Use descriptive names Make check names clear and specific so failures are easy to understand.
Check meaningful conditions Validate actual correctness, not just status codes. Check response content too.
Combine with thresholds Use thresholds to fail tests when check rates drop below acceptable levels.
Keep checks simple Each check should validate one condition. Use multiple checks for complex validation.