Agent skill

Assertion Helper

Guide for writing effective test assertions with clear, meaningful error messages across different testing frameworks

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/testing/assertion-helper-cyperx84-claude-code-plugin-e-c1dca890

SKILL.md

Assertion Helper

Purpose

Help write better assertions that:

  • Clearly express expected behavior
  • Provide actionable error messages
  • Are easy to understand when they fail
  • Cover all edge cases
  • Are framework-appropriate

When to Use

Invoke this skill when:

  • Writing test assertions
  • Debugging failing tests
  • Improving test readability
  • Creating custom matchers
  • Teaching testing best practices

Instructions

Step 1: Choose the Right Assertion

Select assertion based on what you're testing:

  1. Equality: Value comparison
  2. Type: Data type checking
  3. Truthiness: Boolean conditions
  4. Existence: Null/undefined checks
  5. Inclusion: Array/object membership
  6. Exceptions: Error throwing
  7. Async: Promise resolution/rejection

Step 2: Make It Specific

Use the most specific assertion available:

  • toBe(5) > toBeTruthy()
  • toEqual([1,2,3]) > toHaveLength(3)
  • toThrow('Invalid') > toThrow()

Step 3: Add Context

Provide helpful messages for failures:

typescript
expect(result, 'User should be authenticated after login').toBe(true);

Step 4: Test Edge Cases

Include assertions for:

  • Null/undefined
  • Empty values
  • Boundaries (min/max)
  • Invalid inputs

Assertion Patterns

Jest/Vitest

Equality Assertions

typescript
// Primitive values (===)
expect(result).toBe(5);
expect(result).toBe('hello');
expect(result).toBe(true);

// Objects/Arrays (deep equality)
expect(user).toEqual({
  name: 'John',
  age: 30
});

expect(array).toEqual([1, 2, 3]);

// Opposite
expect(result).not.toBe(null);
expect(array).not.toEqual([]);

Type Assertions

typescript
// Null/Undefined
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();

// Truthiness
expect(value).toBeTruthy();
expect(value).toBeFalsy();

// Type checks
expect(typeof result).toBe('string');
expect(Array.isArray(result)).toBe(true);
expect(result instanceof Error).toBe(true);

Number Assertions

typescript
// Comparison
expect(age).toBeGreaterThan(18);
expect(age).toBeGreaterThanOrEqual(18);
expect(age).toBeLessThan(100);
expect(age).toBeLessThanOrEqual(100);

// Floating point
expect(result).toBeCloseTo(0.3, 2); // Within 2 decimal places

// NaN check
expect(result).toBeNaN();

String Assertions

typescript
// Exact match
expect(text).toBe('Hello World');

// Contains
expect(text).toContain('Hello');

// Regex
expect(email).toMatch(/^[a-z]+@[a-z]+\.[a-z]+$/);

// Case insensitive
expect(text.toLowerCase()).toBe('hello');

Array/Object Assertions

typescript
// Array membership
expect(array).toContain(item);
expect(array).toContainEqual({ id: 1 });

// Array length
expect(array).toHaveLength(3);

// Object properties
expect(obj).toHaveProperty('name');
expect(obj).toHaveProperty('address.city', 'NYC');

// Partial object match
expect(user).toMatchObject({
  name: 'John',
  // Other properties ignored
});

// Empty checks
expect(array).toEqual([]);
expect(obj).toEqual({});

Function/Error Assertions

typescript
// Function called
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith(arg1, arg2);
expect(mockFn).toHaveBeenLastCalledWith(arg1, arg2);

// Function throws
expect(() => throwError()).toThrow();
expect(() => throwError()).toThrow(Error);
expect(() => throwError()).toThrow('Error message');
expect(() => throwError()).toThrow(/error/i);

// Return value
expect(mockFn).toHaveReturnedWith(value);

Async Assertions

typescript
// Promise resolves
await expect(promise).resolves.toBe(value);
await expect(promise).resolves.toEqual({ data: 'value' });

// Promise rejects
await expect(promise).rejects.toThrow();
await expect(promise).rejects.toThrow('Error message');

// Async function
it('should fetch user', async () => {
  const user = await fetchUser(1);
  expect(user).toEqual({ id: 1, name: 'John' });
});

Python (pytest)

Basic Assertions

python
# Equality
assert result == 5
assert result != 0

# Identity
assert result is None
assert result is not None

# Boolean
assert is_valid
assert not is_invalid

# Membership
assert item in collection
assert item not in collection

# Type
assert isinstance(result, str)
assert isinstance(result, (int, float))  # Multiple types

Numeric Assertions

python
# Comparison
assert age > 18
assert age >= 18
assert age < 100
assert age <= 100

# Approximate (floating point)
import pytest
assert result == pytest.approx(0.3, abs=0.01)

# Math checks
import math
assert math.isnan(result)
assert math.isinf(result)

String Assertions

python
# Exact match
assert text == "Hello World"

# Contains
assert "Hello" in text

# Regex
import re
assert re.match(r'^[a-z]+@[a-z]+\.[a-z]+$', email)

# Case insensitive
assert text.lower() == "hello"

# Start/End
assert text.startswith("Hello")
assert text.endswith("World")

Collection Assertions

python
# List/Tuple
assert len(collection) == 3
assert collection == [1, 2, 3]
assert collection != []

# Set operations
assert set(collection) == {1, 2, 3}

# Dict
assert 'key' in dictionary
assert dictionary['key'] == 'value'
assert dictionary.get('key') == 'value'

Exception Assertions

python
# Exception raised
with pytest.raises(ValueError):
    raise_error()

# Exception message
with pytest.raises(ValueError, match="Invalid input"):
    raise_error()

# Exception details
with pytest.raises(ValueError) as exc_info:
    raise_error()
assert str(exc_info.value) == "Invalid input"
assert exc_info.type == ValueError

Custom Assertions

python
def assert_valid_user(user):
    """Custom assertion for user validation"""
    assert user is not None, "User should not be None"
    assert 'id' in user, "User should have an id"
    assert 'name' in user, "User should have a name"
    assert isinstance(user['id'], int), "User id should be int"
    assert len(user['name']) > 0, "User name should not be empty"

# Usage
assert_valid_user(result)

Go (testing)

Basic Assertions

go
import "testing"

// Equality
if result != expected {
    t.Errorf("Expected %v, got %v", expected, result)
}

// Boolean
if !isValid {
    t.Error("Expected isValid to be true")
}

// Nil check
if result == nil {
    t.Error("Expected result to not be nil")
}

Helper Functions

go
func assertEqual(t *testing.T, expected, actual interface{}) {
    t.Helper()
    if expected != actual {
        t.Errorf("Expected %v, got %v", expected, actual)
    }
}

func assertNoError(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Errorf("Expected no error, got %v", err)
    }
}

func assertError(t *testing.T, err error, message string) {
    t.Helper()
    if err == nil {
        t.Error("Expected error, got nil")
        return
    }
    if !strings.Contains(err.Error(), message) {
        t.Errorf("Expected error containing %q, got %q", message, err.Error())
    }
}

// Usage
func TestFunction(t *testing.T) {
    result, err := Function()
    assertNoError(t, err)
    assertEqual(t, "expected", result)
}

Best Practices

DO:

✅ Use the most specific assertion ✅ Test one thing per assertion ✅ Add descriptive messages ✅ Test both positive and negative cases ✅ Use custom matchers for repeated patterns ✅ Group related assertions

DON'T:

❌ Use generic assertions (toBeTruthy()) ❌ Multiple unrelated assertions in one test ❌ Assertion without context ❌ Test implementation details ❌ Ignore edge cases ❌ Rely on assertion order

Assertion Patterns

The Positive/Negative Pattern

typescript
// Test both that it works AND doesn't work wrong
it('should validate email', () => {
  expect(validate('test@example.com')).toBe(true);
  expect(validate('invalid')).toBe(false);
});

The Boundary Pattern

typescript
// Test edges of valid ranges
it('should accept ages between 0 and 120', () => {
  expect(isValidAge(0)).toBe(true);
  expect(isValidAge(120)).toBe(true);
  expect(isValidAge(-1)).toBe(false);
  expect(isValidAge(121)).toBe(false);
});

The Null/Undefined Pattern

typescript
// Always test null/undefined cases
it('should handle null input', () => {
  expect(process(null)).toBeNull();
});

it('should handle undefined input', () => {
  expect(process(undefined)).toBeUndefined();
});

The Error Message Pattern

typescript
// Verify error messages for debugging
it('should throw descriptive error', () => {
  expect(() => divide(10, 0))
    .toThrow('Cannot divide by zero');
});

The State Verification Pattern

typescript
// Verify state changes
it('should update state correctly', () => {
  const obj = new Counter();
  expect(obj.count).toBe(0);

  obj.increment();
  expect(obj.count).toBe(1);

  obj.decrement();
  expect(obj.count).toBe(0);
});

Custom Matchers

Jest Custom Matcher

typescript
expect.extend({
  toBeValidEmail(received: string) {
    const pass = /^[a-z]+@[a-z]+\.[a-z]+$/.test(received);
    return {
      pass,
      message: () =>
        pass
          ? `Expected ${received} not to be a valid email`
          : `Expected ${received} to be a valid email`
    };
  }
});

// Usage
expect('test@example.com').toBeValidEmail();

Pytest Custom Assertion

python
def assert_valid_email(email: str) -> None:
    """Assert that email is valid"""
    import re
    pattern = r'^[a-z]+@[a-z]+\.[a-z]+$'
    assert re.match(pattern, email), f"'{email}' is not a valid email"

# Usage
assert_valid_email('test@example.com')

Debugging Failed Assertions

Add Context

typescript
// Bad
expect(result).toBe(5);

// Good
expect(result, 'Should calculate correct total after discount').toBe(5);

Use Better Diff

typescript
// For objects, toEqual gives better diff than toBe
expect(user).toEqual({
  id: 1,
  name: 'John',
  age: 30
});

Break Down Complex Assertions

typescript
// Bad - hard to debug
expect(result).toEqual({ id: 1, name: 'John', address: { city: 'NYC' } });

// Good - easier to see what failed
expect(result.id).toBe(1);
expect(result.name).toBe('John');
expect(result.address.city).toBe('NYC');

Output Format

When providing assertion guidance:

## Assertions for ${TestCase}

**Testing**: ${whatIsBeingTested}

**Recommended Assertions**:
```${language}
${assertions}

Why:

  • ${reason1}
  • ${reason2}

Edge Cases to Test:

  • ${edgeCase1}
  • ${edgeCase2}

## Related Skills

- `test-pattern-library`: For complete test patterns
- `mock-generator`: For mocking dependencies
- `test-data-factory`: For test data generation
- `error-message-writer`: For better error messages

Didn't find tool you were looking for?

Be as detailed as possible for better results