bupkis
    Preparing search index...

    Bupkis: Basic Usage

    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'),
    }),
    });
    });
    });
    1. Reusability: Create assertion functions once and use them across multiple tests
    2. Flexibility: Mix different pattern types (exact values, RegExp, assertions) in the same object
    3. Composability: Nest assertions within other assertions for complex validation
    4. Type Safety: Full TypeScript support with proper type inference
    5. Readable Patterns: Express complex validation rules in a natural, readable way

    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 frameworks
    • message: Human-readable description of what went wrong
    • actual: 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'.