This guide covers the fundamentals of using BUPKIS for assertions in your tests.
Install BUPKIS as a development dependency:
npm install bupkis -D
BUPKIS requires Node.js version ^20.19.0 || ^22.12.0 || >=23.
The library supports both ESM and CommonJS module systems, so you can use it with any modern Node.js project setup.
BUPKIS provides different import strategies depending on your needs:
If you want to start using assertions immediately with the built-in library:
import { expect } from 'bupkis';
For creating custom assertions and accessing all utilities:
import {
expect,
expectAsync,
createAssertion,
createAsyncAssertion,
use,
z,
} from 'bupkis';
You can also import organized namespaces:
import { expect, assertion, guards, schema, util, error } from 'bupkis';
// Use assertion creation utilities
const myAssertion = assertion.createAssertion(['to be rad'], z.boolean());
// Use type guards
if (guards.isString(value)) {
// value is guaranteed to be a string
}
BUPKIS uses a natural language API instead of method chaining. Here's how it works in a typical test:
import { expect } from 'bupkis';
describe('Basic assertions', () => {
it('should validate types', () => {
expect('hello', 'to be a string');
expect(42, 'to be a number');
expect(true, 'to be a boolean');
});
it('should validate values', () => {
expect(5, 'to equal', 5);
expect('hello world', 'to contain', 'world');
expect([1, 2, 3], 'to have length', 3);
});
it('should support negation', () => {
expect(42, 'not to be a string');
expect('hello', 'not to equal', 'goodbye');
});
it('should work with objects', () => {
const user = { name: 'Alice', age: 30 };
expect(user, 'to be an object');
expect(user, 'to have property', 'name');
expect(user, 'to satisfy', { name: 'Alice' });
});
});
BUPKIS provides a powerful feature called embeddable assertions through the expect.it()
function. This allows you to create reusable assertion functions that can be embedded within complex object patterns, particularly useful with the 'to satisfy'
assertion.
The expect.it()
function creates an assertion function that can be used later:
import { expect } from 'bupkis';
describe('Embeddable assertions', () => {
it('should create reusable assertion functions', () => {
// Create an embeddable assertion
const isString = expect.it('to be a string');
const isPositiveNumber = expect.it('to be greater than', 0);
// Use them directly
isString('hello'); // ✓ Passes
isPositiveNumber(42); // ✓ Passes
// These would fail:
// isString(123); // ✗ AssertionError
// isPositiveNumber(-5); // ✗ AssertionError
});
});
The real power comes when using embeddable assertions within object patterns for complex validation:
describe('Object pattern validation', () => {
it('should validate complex objects with embeddable assertions', () => {
const user = {
name: 'Alice Johnson',
email: 'alice@example.com',
age: 30,
roles: ['admin', 'user'],
metadata: {
lastLogin: '2024-01-15',
preferences: {
theme: 'dark',
notifications: true,
},
},
};
// Use embeddable assertions for flexible pattern matching
expect(user, 'to satisfy', {
name: expect.it('to be a string'),
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, // RegExp patterns also work
age: expect.it('to be greater than', 18),
roles: [expect.it('to be a string')], // Each array element must be a string
metadata: {
lastLogin: expect.it('to match', /^\d{4}-\d{2}-\d{2}$/),
preferences: {
theme: expect.it('to be one of', ['light', 'dark']),
notifications: expect.it('to be a boolean'),
},
},
});
});
});
You can mix different types of patterns within the same object:
describe('Mixed pattern validation', () => {
it('should support mixed pattern types', () => {
const apiResponse = {
status: 'success',
data: {
id: 12345,
title: 'Important Document',
tags: ['urgent', 'review'],
},
timestamp: '2024-01-15T10:30:00Z',
};
expect(apiResponse, 'to satisfy', {
status: 'success', // Exact value match
data: {
id: expect.it('to be a number'), // Type assertion
title: expect.it('to contain', 'Document'), // Content assertion
tags: [expect.it('to be a string')], // Array element assertion
},
timestamp: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/, // RegExp pattern
});
});
});
Embeddable assertions work with all BUPKIS assertion types, including complex ones:
describe('Advanced embeddable patterns', () => {
it('should support complex assertion types', () => {
const config = {
database: {
host: 'localhost',
port: 5432,
credentials: {
username: 'admin',
password: 'secret123',
},
},
features: ['auth', 'logging', 'monitoring'],
};
expect(config, 'to satisfy', {
database: {
host: expect.it('to be a string'),
port: expect.it('to be between', 1024, 65535),
credentials: expect.it('to be an object'),
},
features: expect.it('to have length greater than', 2),
});
});
it('should support nested object assertions', () => {
const product = {
name: 'Laptop',
price: 999.99,
specs: {
cpu: 'Intel i7',
ram: '16GB',
storage: '512GB SSD',
},
};
expect(product, 'to satisfy', {
name: expect.it('to be a string'),
price: expect.it('to be a number'),
specs: expect.it('to satisfy', {
cpu: expect.it('to contain', 'Intel'),
ram: expect.it('to match', /^\d+GB$/),
storage: expect.it('to be a string'),
}),
});
});
});
The embeddable assertions feature makes BUPKIS particularly powerful for API response validation, configuration testing, and any scenario where you need to validate complex nested data structures.
When an assertion fails, BUPKIS throws a standard AssertionError
that's compatible with all major testing frameworks. Here are real examples of what these errors look like:
expect(42, 'to be a string');
// AssertionError: Assertion "{unknown} 'to be a string'" failed: Invalid input: expected string, received number
// actual: 42
// expected: []
expect('hello', 'to equal', 'goodbye');
// AssertionError: Expected 'hello' to equal 'goodbye'
// actual: hello
// expected: goodbye
expect([1, 2, 3], 'to have length', 5);
// AssertionError: Assertion "{array} 'to have length' {number}" failed for arguments: [ [ 1, 2, 3 ], 'to have length', 5 ]
const user = { name: 'Alice', age: 30, role: 'user' };
expect(user, 'to satisfy', {
name: 'Bob',
age: 25,
role: 'admin',
department: 'engineering',
});
// AssertionError: Assertion "{object}! 'to satisfy' / 'to be like' {object}" failed: ; department: Invalid input: expected string, received undefined
// actual: {
// "name": "Alice",
// "age": 30,
// "role": "user"
// }
// expected: {
// "name": "Bob",
// "age": 25,
// "role": "admin",
// "department": "engineering"
// }
All BUPKIS assertion errors include these standard properties:
name
: Always 'AssertionError'
for compatibility with testing frameworksmessage
: Human-readable description of what went wrongactual
: The value that was tested (when available)expected
: The expected value or pattern (when available)The error messages are powered by Zod's validation system, providing detailed context about exactly why an assertion failed.
The natural language approach makes tests more readable and self-documenting. Instead of remembering method names like toBeInstanceOf()
or toHaveProperty()
, you write what you mean: 'to be an instance of'
or 'to have property'
.