| name | Assertion Helper |
| description | Guide for writing effective test assertions with clear, meaningful error messages across different testing frameworks |
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:
- Equality: Value comparison
- Type: Data type checking
- Truthiness: Boolean conditions
- Existence: Null/undefined checks
- Inclusion: Array/object membership
- Exceptions: Error throwing
- 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:
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
expect(result).toBe(5);
expect(result).toBe('hello');
expect(result).toBe(true);
expect(user).toEqual({
name: 'John',
age: 30
});
expect(array).toEqual([1, 2, 3]);
expect(result).not.toBe(null);
expect(array).not.toEqual([]);
Type Assertions
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(typeof result).toBe('string');
expect(Array.isArray(result)).toBe(true);
expect(result instanceof Error).toBe(true);
Number Assertions
expect(age).toBeGreaterThan(18);
expect(age).toBeGreaterThanOrEqual(18);
expect(age).toBeLessThan(100);
expect(age).toBeLessThanOrEqual(100);
expect(result).toBeCloseTo(0.3, 2);
expect(result).toBeNaN();
String Assertions
expect(text).toBe('Hello World');
expect(text).toContain('Hello');
expect(email).toMatch(/^[a-z]+@[a-z]+\.[a-z]+$/);
expect(text.toLowerCase()).toBe('hello');
Array/Object Assertions
expect(array).toContain(item);
expect(array).toContainEqual({ id: 1 });
expect(array).toHaveLength(3);
expect(obj).toHaveProperty('name');
expect(obj).toHaveProperty('address.city', 'NYC');
expect(user).toMatchObject({
name: 'John',
});
expect(array).toEqual([]);
expect(obj).toEqual({});
Function/Error Assertions
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith(arg1, arg2);
expect(mockFn).toHaveBeenLastCalledWith(arg1, arg2);
expect(() => throwError()).toThrow();
expect(() => throwError()).toThrow(Error);
expect(() => throwError()).toThrow('Error message');
expect(() => throwError()).toThrow(/error/i);
expect(mockFn).toHaveReturnedWith(value);
Async Assertions
await expect(promise).resolves.toBe(value);
await expect(promise).resolves.toEqual({ data: 'value' });
await expect(promise).rejects.toThrow();
await expect(promise).rejects.toThrow('Error message');
it('should fetch user', async () => {
const user = await fetchUser(1);
expect(user).toEqual({ id: 1, name: 'John' });
});
Python (pytest)
Basic Assertions
assert result == 5
assert result != 0
assert result is None
assert result is not None
assert is_valid
assert not is_invalid
assert item in collection
assert item not in collection
assert isinstance(result, str)
assert isinstance(result, (int, float))
Numeric Assertions
assert age > 18
assert age >= 18
assert age < 100
assert age <= 100
import pytest
assert result == pytest.approx(0.3, abs=0.01)
import math
assert math.isnan(result)
assert math.isinf(result)
String Assertions
assert text == "Hello World"
assert "Hello" in text
import re
assert re.match(r'^[a-z]+@[a-z]+\.[a-z]+$', email)
assert text.lower() == "hello"
assert text.startswith("Hello")
assert text.endswith("World")
Collection Assertions
assert len(collection) == 3
assert collection == [1, 2, 3]
assert collection != []
assert set(collection) == {1, 2, 3}
assert 'key' in dictionary
assert dictionary['key'] == 'value'
assert dictionary.get('key') == 'value'
Exception Assertions
with pytest.raises(ValueError):
raise_error()
with pytest.raises(ValueError, match="Invalid input"):
raise_error()
with pytest.raises(ValueError) as exc_info:
raise_error()
assert str(exc_info.value) == "Invalid input"
assert exc_info.type == ValueError
Custom Assertions
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"
assert_valid_user(result)
Go (testing)
Basic Assertions
import "testing"
if result != expected {
t.Errorf("Expected %v, got %v", expected, result)
}
if !isValid {
t.Error("Expected isValid to be true")
}
if result == nil {
t.Error("Expected result to not be nil")
}
Helper Functions
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())
}
}
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
it('should validate email', () => {
expect(validate('test@example.com')).toBe(true);
expect(validate('invalid')).toBe(false);
});
The Boundary Pattern
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
it('should handle null input', () => {
expect(process(null)).toBeNull();
});
it('should handle undefined input', () => {
expect(process(undefined)).toBeUndefined();
});
The Error Message Pattern
it('should throw descriptive error', () => {
expect(() => divide(10, 0))
.toThrow('Cannot divide by zero');
});
The State Verification Pattern
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
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`
};
}
});
expect('test@example.com').toBeValidEmail();
Pytest Custom Assertion
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"
assert_valid_email('test@example.com')
Debugging Failed Assertions
Add Context
expect(result).toBe(5);
expect(result, 'Should calculate correct total after discount').toBe(5);
Use Better Diff
expect(user).toEqual({
id: 1,
name: 'John',
age: 30
});
Break Down Complex Assertions
expect(result).toEqual({ id: 1, name: 'John', address: { city: 'NYC' } });
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:
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