Testing Your Code
Testing in Jac
Section titled “Testing in Jac”Learn how to write and run tests for your Jac code.
Prerequisites
- Completed: Jac Basics
- Time: ~20 minutes
Writing Tests
Section titled “Writing Tests”Jac uses the test keyword to define tests:
def add(a: int, b: int) -> int { return a + b;}
test "add" { assert add(2, 3) == 5; assert add(-1, 1) == 0; assert add(0, 0) == 0;}Note: Test names are string descriptions, giving you readable names with spaces and punctuation.
Running Tests
Section titled “Running Tests”# Run all tests in a filejac test main.jac
# Run tests in a directoryjac test -d tests/
# Run a specific testjac test main.jac -t test_add
# Verbose outputjac test main.jac -v
# Stop on first failurejac test main.jac -xTest Output
Section titled “Test Output”unittest.case.FunctionTestCase (test_add) ... okunittest.case.FunctionTestCase (test_subtract) ... ok
----------------------------------------------------------------------Ran 2 tests in 0.001s
OKAssertions
Section titled “Assertions”Basic Assertions
Section titled “Basic Assertions”test "assertions" { # Equality assert 1 + 1 == 2; assert "hello" == "hello";
# Inequality assert 5 != 3;
# Comparisons assert 5 > 3; assert 3 < 5; assert 5 >= 5; assert 5 <= 5;
# Boolean assert True; assert not False;
# Membership assert 3 in [1, 2, 3]; assert "key" in {"key": "value"};}Assertions with Messages
Section titled “Assertions with Messages”test "with messages" { result = calculate_something(); assert result > 0, f"Expected positive, got {result}";}Testing Objects
Section titled “Testing Objects”obj Calculator { has value: int = 0;
def add(n: int) -> int { self.value += n; return self.value; }
def reset() -> None { self.value = 0; }}
test "calculator add" { calc = Calculator(); assert calc.add(5) == 5; assert calc.add(3) == 8; assert calc.value == 8;}
test "calculator reset" { calc = Calculator(); calc.add(10); calc.reset(); assert calc.value == 0;}Testing Nodes and Walkers
Section titled “Testing Nodes and Walkers”node Counter { has count: int = 0;}
walker Incrementer { has amount: int = 1;
can start with Root entry { visit [-->]; }
can increment with Counter entry { here.count += self.amount; }}
test "walker increments counter" { # Create graph counter = root ++> Counter();
# Spawn walker root spawn Incrementer();
# Verify assert counter[0].count == 1;}
test "walker with custom amount" { counter = root ++> Counter();
root spawn Incrementer(amount=5);
assert counter[0].count == 5;}Testing Walker Reports
Section titled “Testing Walker Reports”node Person { has name: str; has age: int;}
walker FindAdults { can check with Root entry { for person in [-->](?:Person) { if person.age >= 18 { report person; } } }}
test "find adults" { root ++> Person(name="Alice", age=30); root ++> Person(name="Bob", age=15); root ++> Person(name="Carol", age=25);
result = root spawn FindAdults();
assert len(result.reports) == 2;
names = [p.name for p in result.reports]; assert "Alice" in names; assert "Carol" in names; assert "Bob" not in names;}Testing Graph Structure
Section titled “Testing Graph Structure”node Room { has name: str;}
edge Door {}
test "graph connections" { kitchen = Room(name="Kitchen"); living = Room(name="Living Room"); bedroom = Room(name="Bedroom");
root ++> kitchen; kitchen +>: Door() :+> living; living +>: Door() :+> bedroom;
# Test connections assert len([root -->]) == 1; assert len([kitchen -->]) == 1; assert len([living -->]) == 1; assert len([bedroom -->]) == 0;
# Test connectivity assert living in [kitchen ->:Door:->]; assert bedroom in [living ->:Door:->];}Test Organization
Section titled “Test Organization”Separate Test Files
Section titled “Separate Test Files”myproject/├── jac.toml├── src/│ ├── models.jac│ └── walkers.jac└── tests/ ├── test_models.jac └── test_walkers.jac# Run all testsjac test -d tests/
# Run specific test filejac test tests/test_models.jacTests in Same File
Section titled “Tests in Same File”# models.jacobj User { has name: str; has email: str;
def is_valid() -> bool { return len(self.name) > 0 and "@" in self.email; }}
# Tests at bottom of filetest "user valid" { user = User(name="Alice", email="alice@example.com"); assert user.is_valid();}
test "user invalid email" { user = User(name="Alice", email="invalid"); assert not user.is_valid();}
test "user empty name" { user = User(name="", email="alice@example.com"); assert not user.is_valid();}CLI Options
Section titled “CLI Options”| Option | Short | Description |
|---|---|---|
--test_name | -t | Run specific test by name |
--filter | -f | Filter tests by pattern |
--xit | -x | Exit on first failure |
--maxfail | -m | Stop after N failures |
--directory | -d | Test directory |
--verbose | -v | Verbose output |
# Examplesjac test main.jac -t test_add -vjac test main.jac -f "test_user" -xjac test -d tests/ -m 3Configuration
Section titled “Configuration”Set test defaults in jac.toml:
[test]directory = "tests"verbose = truefail_fast = falsemax_failures = 10Best Practices
Section titled “Best Practices”1. Descriptive Test Names
Section titled “1. Descriptive Test Names”# Good - readable descriptionstest "user creation with valid email" { }test "walker visits all connected nodes" { }
# Avoid - vague or cryptictest "t1" { }test "thing" { }2. One Assertion Focus
Section titled “2. One Assertion Focus”# Good - focused teststest "add positive numbers" { assert add(2, 3) == 5;}
test "add negative numbers" { assert add(-2, -3) == -5;}
# Avoid - too many unrelated assertionstest "all math operations" { assert add(2, 3) == 5; assert subtract(5, 3) == 2; assert multiply(2, 3) == 6;}3. Isolate Tests
Section titled “3. Isolate Tests”# Good - creates fresh statetest "counter increment" { counter = root ++> Counter(); root spawn Incrementer(); assert counter[0].count == 1;}
# Each test should be independenttest "counter starts at zero" { counter = Counter(); assert counter.count == 0;}4. Test Edge Cases
Section titled “4. Test Edge Cases”test "divide normal" { assert divide(10, 2) == 5;}
test "divide by zero" { try { divide(10, 0); assert False, "Should have raised error"; } except ZeroDivisionError { assert True; # Expected }}
test "divide negative" { assert divide(-10, 2) == -5;}Next Steps
Section titled “Next Steps”- Language Reference: Testing - Complete testing reference
- AI Integration - Test AI-integrated functions
- Production Deployment - Run as API server