Skip to content

Tests#

Code Example

Runnable Example in Jac and JacLib

# Tests - Unit testing with test blocks

# Named test with assertion
test test1 {
    assert almostEqual(4.99999, 4.99999);
}

# Another test
test test2 {
    assert 5 == 5;
}

# Test with membership check
test test3 {
    assert "e" in "qwerty";
}

with entry:__main__ {
    # Run tests using jac test command
    import subprocess;
    result = subprocess.run(
        ["jac", "test", f"{__file__}"],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
    );
    print(result.stderr);
}
# Tests - Unit testing with test blocks

# Named test with assertion
test test1 {
    assert almostEqual(4.99999, 4.99999);
}

# Another test
test test2 {
    assert 5 == 5;
}

# Test with membership check
test test3 {
    assert "e" in "qwerty";
}

with entry:__main__ {
    # Run tests using jac test command
    import subprocess;
    result = subprocess.run(
        ["jac", "test", f"{__file__}"],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
    );
    print(result.stderr);
}
from __future__ import annotations
from jaclang.runtimelib.builtin import *
from jaclang import JacMachineInterface as _jl

@_jl.jac_test
def test_test1(_check) -> None:
    _check.assertAlmostEqual(4.99999, 4.99999)

@_jl.jac_test
def test_test2(_check) -> None:
    _check.assertEqual(5, 5)

@_jl.jac_test
def test_test3(_check) -> None:
    _check.assertIn('e', 'qwerty')
if __name__ == '__main__':
    import subprocess
    result = subprocess.run(['jac', 'test', f'{__file__}'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    print(result.stderr)
Jac Grammar Snippet
test: KW_TEST NAME? code_block

Description

Tests - Unit Testing with Test Blocks

Jac provides built-in support for unit testing through test blocks, allowing you to write tests directly in source files alongside the code being tested.

Test Block Syntax

The general form is: test test_name { assertions and test code }

Components: - test - Keyword that begins a test block - test_name - Identifier for the test - Curly braces - Contain test code and assertions

Test Examples

Lines 4-6 define test1. This test uses almostEqual() to check if two floating-point numbers are approximately equal. This function is useful for comparing floats where exact equality might fail due to precision issues.

Lines 9-11 define test2. This uses a simple equality assertion to verify that 5 equals itself.

Lines 14-16 define test3. This uses a membership assertion to verify that the character "e" exists in the string "qwerty".

Assertion Behavior

Condition Result Behavior
True Test continues, eventually passes
False AssertionError raised, test fails

Assertions are the primary mechanism for verifying expected behavior. Each test can contain multiple assertions.

Running Tests

Lines 18-26 show programmatic test execution. Line 18 uses with entry:__main__ to ensure code only runs when the file is executed directly (not imported).

Lines 21-24 use subprocess.run() to execute the Jac test runner: - ["jac", "test", f"{__file__}"] - Command to test current file - stdout=subprocess.PIPE, stderr=subprocess.PIPE - Capture output - text=True - Return output as strings

Line 25 prints the test results from stderr, where test output is sent.

Test Discovery and Execution

graph TD
    A[jac test filename.jac] --> B[Scan file for test blocks]
    B --> C[Execute test1]
    B --> D[Execute test2]
    B --> E[Execute test3]
    C --> F{Assertions pass?}
    D --> G{Assertions pass?}
    E --> H{Assertions pass?}
    F --> I[Report Results]
    G --> I
    H --> I
    I --> J[Display Summary]

When jac test filename.jac runs: 1. Jac scans the file for all test blocks 2. Each test block executes in isolation 3. Assertions are evaluated 4. Results are reported (pass/fail per test) 5. Summary shows total tests and any failures

Test Isolation

Each test block runs independently: - If one test fails, others still execute - Tests don't share state - Execution order is not guaranteed - A single failure doesn't prevent other tests from running

Best Practices

Effective tests should:

Practice Description
Independence Don't rely on other tests or execution order
Descriptive names Use names that describe what is tested
Single behavior Test one specific scenario per test
Meaningful assertions Verify actual behavior, not trivial truths
Fast execution Keep tests quick to encourage frequent running
Cover edge cases Test both normal and boundary conditions

Use Cases

Test blocks are valuable for: - Unit testing individual functions or methods - Verifying object behavior - Testing graph operations and walker logic - Regression testing to prevent breaking changes - Documentation through executable examples

Test Naming

While the examples use simple names (test1, test2, test3), production code should use descriptive names: - test_password_validation - Clearer than test1 - test_user_creation - Describes what is tested - test_edge_traversal - Indicates the feature

Integration with Development

Tests written inline with code: - Live alongside the implementation - Are easy to discover and maintain - Serve as executable documentation - Enable test-driven development workflows - Provide immediate feedback during development