How to skip or fail Tests in Hooks
Skipping Tests
Section titled “Skipping Tests”Use utils.skip() when a test cannot run due to missing preconditions.
Reason to Skip
Section titled “Reason to Skip”- Required resources cannot be created
- Prerequisites are not met
- Test is not applicable for certain conditions
Basic Usage
Section titled “Basic Usage”import { beforeEach } from '@thymian/hooks';
export const seed = beforeEach( 'GET /launches/{id} -> 200 (application/json)', async (request, ctx, utils) => { const response = await utils.request( 'POST /launches (application/json) -> 201 (application/json)', { body: { missionName: utils.randomString(10), launchDate: '2026-12-31T00:00:00Z', rocketType: 'Falcon9', }, headers: { 'content-type': 'application/json' }, }, );
// Skip if resource creation failed if (response.statusCode !== 201) { utils.skip('Cannot create required launch resource'); }
request.pathParameters.id = response.body.id; },);Skip with Details
Section titled “Skip with Details”Provide detailed information about why the test was skipped:
if (response.statusCode === 503) { utils.skip( `Service unavailable (status ${response.statusCode}). Cannot proceed with test.`, );}Failing Tests
Section titled “Failing Tests”Use utils.fail() when a test should explicitly fail due to invalid conditions.
When to Fail
Section titled “When to Fail”- Setup produces unexpected results
- Resources are in an invalid state
- Critical preconditions are violated
- Data validation fails
Basic Usage
Section titled “Basic Usage”import { beforeEach } from '@thymian/hooks';
export const seed = beforeEach( 'GET /launches/{id} -> 200 (application/json)', async (request, ctx, utils) => { const response = await utils.request( 'POST /launches (application/json) -> 201 (application/json)', { body: { missionName: utils.randomString(10), launchDate: '2026-12-31T00:00:00Z', rocketType: 'Falcon9', }, headers: { 'content-type': 'application/json' }, }, );
// Fail if creation returned a status other than the one declared here. // Branch on the declared status and narrow before touching `body` — the // type of `response` is a union of every response the operation // declares, so a status that operation never declares (like `200` for // an endpoint that only declares `201`) is a compile error, and `body` // is only the 201 shape once the branch has narrowed to it. if (response.statusCode !== 201) { utils.fail(`Expected 201 Created, got ${response.statusCode}`); } else if (!response.body.id) { utils.fail('Created launch has no ID field'); } else { request.pathParameters.id = response.body.id; } },);What Happens When You Skip or Fail?
Section titled “What Happens When You Skip or Fail?”utils.skip('Resource unavailable');Result:
- Test is marked as skipped
- Remaining hooks do not execute
- Test does not count as failure
- Test report shows skip reason
Output:
GET /launches/123 ⊘ Skipped: Resource unavailableutils.fail('Invalid response structure');Result:
- Test is marked as failed
- Remaining hooks do not execute
- Test counts as failure in report
- Test report shows failure reason
Output:
GET /launches/123 ✗ Failed: Invalid response structureTechnical Details
Section titled “Technical Details”Both skip() and fail() throw special errors that are caught by the hook runner:
// These throw errors internallyutils.skip('message'); // Throws SkipErrorutils.fail('message'); // Throws FailErrorAlternatives
Section titled “Alternatives”Narrowing the target instead
Section titled “Narrowing the target instead”export const seed = beforeEach( { path: '/launches/{id}', statusClass: '2XX' }, async (request, ctx, utils) => { // Only the successful cases need a resource to exist. },);Narrowing the target is almost always better than branching inside the hook: the hook simply never runs where it does not apply, and a filter value that stops existing is a compile error, whereas a branch is just code that quietly stops matching.
Using Assertions
Section titled “Using Assertions”For validation in afterEach hooks, consider using utils.assertionFailure() instead of utils.fail():
export const assertShape = afterEach( 'GET /launches/{id} -> 200 (application/json)', (response, ctx, utils) => { const body = JSON.parse(response.body ?? '{}');
if (!body.id) { utils.assertionFailure('Missing ID field', { expected: 'id field present', actual: 'id field missing', }); } },);Difference: assertionFailure() records a failed assertion but allows other validations to continue. fail() stops execution immediately.