Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Assertions & Verification

🔧 HOW-TO | 📚 REFERENCE | Write clear, effective assertions

Assertions verify that code behaves correctly. Chicago TDD Tools provides helpers for clear, readable assertions.

Basic Assertions

Standard Assertions

#![allow(unused)]
fn main() {
use chicago_tdd_tools::prelude::*;

test!(test_basic_assertions, {
    let result: Result<u32, String> = Ok(42);

    // ✅ Chicago TDD style - clear intent
    assert_ok!(&result);
    assert_eq!(result.unwrap(), 42);

    // Error result
    let error: Result<u32, String> = Err("failed".to_string());
    assert_err!(&error);
});
}

Common Assertion Helpers

#![allow(unused)]
fn main() {
// Success/Error checks
assert_ok!(&result);           // Verifies Ok(_)
assert_err!(&result);          // Verifies Err(_)

// Equality
assert_eq!(actual, expected);   // Equality check
assert_ne!(actual, expected);   // Inequality check

// Boolean
assert!(condition);             // Checks true
assert!(!condition);            // Checks false
}

Numeric Assertions

Range Checking

#![allow(unused)]
fn main() {
test!(test_numeric_assertions, {
    let value = 42;

    // Standard assertions
    assert_eq!(value, 42);
    assert!(value > 40);
    assert!(value < 50);

    // Chicago TDD helper
    assert_in_range!(value, 40, 50);  // Inclusive range
});
}

Floating Point

#![allow(unused)]
fn main() {
test!(test_floating_point, {
    let value: f64 = 3.14159;
    let expected: f64 = 3.14;

    // ✅ Epsilon comparison (handles rounding errors)
    assert!((value - expected).abs() < 0.01);
});
}

String Assertions

#![allow(unused)]
fn main() {
test!(test_string_assertions, {
    let text = "Hello, World!";

    // Equality
    assert_eq!(text, "Hello, World!");

    // Containment
    assert!(text.contains("World"));

    // Pattern matching
    assert!(text.starts_with("Hello"));
    assert!(text.ends_with("!"));
});
}

Collection Assertions

#![allow(unused)]
fn main() {
test!(test_collection_assertions, {
    let vec = vec![1, 2, 3];

    // Length
    assert_eq!(vec.len(), 3);

    // Containment
    assert!(vec.contains(&2));

    // Empty check
    assert!(!vec.is_empty());
});
}

Option/Result Assertions

#![allow(unused)]
fn main() {
test!(test_option_result_assertions, {
    let some_value: Option<i32> = Some(42);
    let none_value: Option<i32> = None;

    // Option checks
    assert!(some_value.is_some());
    assert!(none_value.is_none());

    // Result checks
    let ok_result: Result<i32, String> = Ok(42);
    let err_result: Result<i32, String> = Err("error".to_string());

    assert!(ok_result.is_ok());
    assert!(err_result.is_err());
});
}

Custom Messages

Add context to assertions:

#![allow(unused)]
fn main() {
test!(test_with_messages, {
    let result = divide(10, 2);
    assert_eq!(result, 5, "Expected division to work correctly");

    let empty = vec![];
    assert!(!empty.is_empty(), "Vector should not be empty after initialization");
});
}

Real-World Example: User Creation

#![allow(unused)]
fn main() {
test!(test_user_creation, {
    // Create user
    let user_data = TestDataBuilder::new()
        .with_var("name", "Alice")
        .with_var("email", "alice@example.com")
        .build_json()?;

    let result = create_user(&user_data)?;

    // Assert success
    assert_ok!(&result);
    let user = result.unwrap();

    // Assert properties
    assert_eq!(user.name, "Alice");
    assert_eq!(user.email, "alice@example.com");
    assert!(user.id > 0);
    assert!(user.created_at.len() > 0);
});
}

Real-World Example: Collection Processing

#![allow(unused)]
fn main() {
test!(test_filter_operations, {
    let numbers = vec![1, 2, 3, 4, 5];

    // Filter evens
    let evens: Vec<_> = numbers
        .iter()
        .filter(|n| n % 2 == 0)
        .cloned()
        .collect();

    // Assertions
    assert_eq!(evens.len(), 2);
    assert!(evens.contains(&2));
    assert!(evens.contains(&4));
    assert!(!evens.contains(&1));  // 1 is odd
});
}

Assert Patterns

Pattern: Positive Case

#![allow(unused)]
fn main() {
test!(test_positive, {
    let result = parse_number("42");
    assert_ok!(&result);
    assert_eq!(result.unwrap(), 42);
});
}

Pattern: Negative Case

#![allow(unused)]
fn main() {
test!(test_negative, {
    let result = parse_number("invalid");
    assert_err!(&result);
});
}

Pattern: Boundary Case

#![allow(unused)]
fn main() {
test!(test_boundary, {
    // Zero
    assert_ok!(&parse_number("0"));

    // Large value
    assert_ok!(&parse_number("999999"));

    // Negative
    assert_ok!(&parse_number("-42"));
});
}

Avoiding Common Pitfalls

❌ Don't: Use unwrap() in assertions

#![allow(unused)]
fn main() {
// Bad - panics if result is Err
let value = result.unwrap();
assert_eq!(value, 42);
}

✅ Do: Check first

#![allow(unused)]
fn main() {
// Good - checks properly
assert_ok!(&result);
if let Ok(value) = result {
    assert_eq!(value, 42);
}
}

❌ Don't: Assert implementation details

#![allow(unused)]
fn main() {
// Bad - depends on internal structure
assert_eq!(user.internal_id, 123);
}

✅ Do: Assert behavior

#![allow(unused)]
fn main() {
// Good - asserts external behavior
assert_eq!(user.name, "Alice");
assert_eq!(user.email, "alice@example.com");
}

Assertion Order

Follow AAA pattern:

  1. Arrange - Build data
  2. Act - Execute code
  3. Assert - Verify behavior
#![allow(unused)]
fn main() {
test!(test_order, {
    // Arrange
    let input = 5;

    // Act
    let result = multiply_by_two(input);

    // Assert (all together, at the end)
    assert_eq!(result, 10);
    assert!(result > 0);
    assert!(result < 100);
});
}

Best Practices

Do:

  • Use helper macros (assert_ok!, assert_err!)
  • Assert one behavior per test
  • Add context with messages
  • Check both success and error paths
  • Use descriptive assertion messages

Don't:

  • Use unwrap() in assertions
  • Mix act and assert
  • Assert implementation details
  • Skip error case assertions
  • Use vague assertion messages

Common Assertions Reference

PatternCode
Value matchesassert_eq!(actual, expected)
Value differentassert_ne!(actual, expected)
True conditionassert!(condition)
Ok resultassert_ok!(&result)
Err resultassert_err!(&result)
In rangeassert_in_range!(value, min, max)
Contains textassert!(text.contains("substring"))
Empty collectionassert!(collection.is_empty())

Next Steps

Learn about error paths: Error Path Testing


Summary

Chicago TDD emphasizes:

  • Clear assertions with helper macros
  • Both success and error case verification
  • AAA pattern (Arrange-Act-Assert)
  • Behavior verification, not implementation details