Testing

The XXML Test Framework provides reflection-based test discovery and execution with assertion utilities. Use the @Test annotation and method naming conventions to define tests.

xxml
#import Language::Core;
#import Language::Test;

Quick Start

xxml
1#import Language::Core;
2#import Language::Test;
3
4// Mark class as a test suite
5@Test
6[ Class <MathTests> Final Extends None
7 [ Public <>
8 // Test methods start with "test"
9 Method <testAddition> Returns None Parameters () Do {
10 Instantiate Integer^ As <result> = Integer::Constructor(2).add(Integer::Constructor(2));
11 Run Assert::equals(
12 Integer::Constructor(4),
13 result,
14 String::Constructor("2+2 should equal 4")
15 );
16 }
17
18 Method <testSubtraction> Returns None Parameters () Do {
19 Instantiate Integer^ As <result> = Integer::Constructor(5).subtract(Integer::Constructor(3));
20 Run Assert::equals(
21 Integer::Constructor(2),
22 result,
23 String::Constructor("5-3 should equal 2")
24 );
25 }
26 ]
27]
28
29[ Entrypoint {
30 Instantiate TestRunner^ As <runner> = TestRunner::Constructor();
31 Instantiate Integer^ As <exitCode> = runner.run(String::Constructor("MathTests"));
32 Exit(exitCode.toInt64());
33}]

Test Discovery

The framework uses reflection to automatically discover test methods. Any method whose name starts with test is considered a test method.

@Test Annotation

Use the @Test annotation to mark classes as test suites:

xxml
@Test
[ Class <MyTestSuite> Final Extends None
    [ Public <>
        Method <testFeatureA> Returns None Parameters () Do { ... }
        Method <testFeatureB> Returns None Parameters () Do { ... }
        Method <helperMethod> Returns None Parameters () Do { ... }  // Not a test
    ]
]

Note

Only methods starting with "test" are discovered and executed. Helper methods and setup/teardown methods use different naming conventions.

Assert

Static assertion methods for verifying test conditions.

Assertion Methods

MethodParametersReturnsDescription
isTruecondition: Bool&, message: String&NoneAssert condition is true
isFalsecondition: Bool&, message: String&NoneAssert condition is false
equalsexpected: Integer&, actual: Integer&, message: String&NoneAssert integers are equal
equalsStringexpected: String&, actual: String&, message: String&NoneAssert strings are equal
notNullmessage: String&NoneReserved for nullable types
failmessage: String&NoneExplicitly fail the test
xxml
// Boolean assertions
Run Assert::isTrue(result.greaterThan(Integer::Constructor(0)), String::Constructor("Result should be positive"));
Run Assert::isFalse(list.isEmpty(), String::Constructor("List should not be empty"));

// Equality assertions
Run Assert::equals(
    Integer::Constructor(42),
    calculator.compute(),
    String::Constructor("Computation result")
);

Run Assert::equalsString(
    String::Constructor("hello"),
    greeting.toLowerCase(),
    String::Constructor("Greeting should be lowercase")
);

// Explicit failure
If (someCondition) -> {
    Run Assert::fail(String::Constructor("This condition should not occur"));
}

TestResult

Represents the result of a single test execution.

Properties

PropertyTypeDescription
testNameString^Name of the test method
passedBool^Whether the test passed
messageString^Success/failure message
durationMsInteger^Execution time in milliseconds

TestSummary

Aggregates results from multiple tests.

Properties

PropertyTypeDescription
totalTestsInteger^Total number of tests run
passedTestsInteger^Number of passing tests
failedTestsInteger^Number of failing tests
resultsList<TestResult>^Individual test results

Methods

MethodParametersReturnsDescription
addResultresult: TestResult^NoneAdd a test result
allPassedBool^Check if all tests passed
printSummaryNonePrint formatted summary

Sample Output

text
=== Test Summary ===
Total:  5
Passed: 4
Failed: 1

Failed tests:
  - testDivision: expected 3 but got 2

Some tests failed.

TestRunner

The main test execution engine.

Methods

MethodParametersReturnsDescription
ConstructorTestRunner^Create test runner
runTestClasstypeName: String^TestSummary^Run all tests in a class
runtypeName: String^Integer^Run tests and return exit code
failmessage: String^NoneMark current test as failed
xxml
Instantiate TestRunner^ As <runner> = TestRunner::Constructor();

// Option 1: Get detailed summary
Instantiate TestSummary^ As <summary> = runner.runTestClass(String::Constructor("MyTests"));
Run summary.printSummary();

// Option 2: Run and get exit code directly
Instantiate Integer^ As <exitCode> = runner.run(String::Constructor("MyTests"));
// exitCode is 0 if all passed, 1 if any failed

Test Method Conventions

  • Naming: Methods must start with test to be discovered
  • Return Type: Should return None
  • Parameters: Should take no parameters
  • Independence: Each test should be independent of others

Complete Example

list_tests.xxml
1#import Language::Core;
2#import Language::Collections;
3#import Language::Test;
4
5@Test
6[ Class <ListTests> Final Extends None
7 [ Public <>
8 Method <testEmptyList> Returns None Parameters () Do {
9 Instantiate List<Integer>^ As <list> = List<Integer>::Constructor();
10 Run Assert::equals(
11 Integer::Constructor(0),
12 list.size(),
13 String::Constructor("New list should be empty")
14 );
15 }
16
17 Method <testAddElement> Returns None Parameters () Do {
18 Instantiate List<Integer>^ As <list> = List<Integer>::Constructor();
19 Run list.add(Integer::Constructor(42));
20
21 Run Assert::equals(
22 Integer::Constructor(1),
23 list.size(),
24 String::Constructor("List should have one element")
25 );
26
27 Run Assert::equals(
28 Integer::Constructor(42),
29 list.get(Integer::Constructor(0)),
30 String::Constructor("Element should be 42")
31 );
32 }
33
34 Method <testClear> Returns None Parameters () Do {
35 Instantiate List<Integer>^ As <list> = List<Integer>::Constructor();
36 Run list.add(Integer::Constructor(1));
37 Run list.add(Integer::Constructor(2));
38 Run list.clear();
39
40 Run Assert::equals(
41 Integer::Constructor(0),
42 list.size(),
43 String::Constructor("Cleared list should be empty")
44 );
45 }
46 ]
47]
48
49[ Entrypoint {
50 Instantiate TestRunner^ As <runner> = TestRunner::Constructor();
51 Instantiate Integer^ As <exitCode> = runner.run(String::Constructor("ListTests"));
52 Exit(exitCode.toInt64());
53}]

Running Tests

bash
# Compile test file
xxml MyTests.XXML -o mytests.exe

# Run tests
./mytests.exe

# Check exit code (0 = all passed, 1 = failures)
echo $?

See Also