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
| Method | Parameters | Returns | Description |
|---|---|---|---|
| isTrue | condition: Bool&, message: String& | None | Assert condition is true |
| isFalse | condition: Bool&, message: String& | None | Assert condition is false |
| equals | expected: Integer&, actual: Integer&, message: String& | None | Assert integers are equal |
| equalsString | expected: String&, actual: String&, message: String& | None | Assert strings are equal |
| notNull | message: String& | None | Reserved for nullable types |
| fail | message: String& | None | Explicitly 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
| Property | Type | Description |
|---|---|---|
testName | String^ | Name of the test method |
passed | Bool^ | Whether the test passed |
message | String^ | Success/failure message |
durationMs | Integer^ | Execution time in milliseconds |
TestSummary
Aggregates results from multiple tests.
Properties
| Property | Type | Description |
|---|---|---|
totalTests | Integer^ | Total number of tests run |
passedTests | Integer^ | Number of passing tests |
failedTests | Integer^ | Number of failing tests |
results | List<TestResult>^ | Individual test results |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
| addResult | result: TestResult^ | None | Add a test result |
| allPassed | — | Bool^ | Check if all tests passed |
| printSummary | — | None | Print 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
| Method | Parameters | Returns | Description |
|---|---|---|---|
| Constructor | — | TestRunner^ | Create test runner |
| runTestClass | typeName: String^ | TestSummary^ | Run all tests in a class |
| run | typeName: String^ | Integer^ | Run tests and return exit code |
| fail | message: String^ | None | Mark 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 failedTest Method Conventions
- Naming: Methods must start with
testto 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 $?