Advanced topics Automated testing Console and GUI

Testing Klyn Applications

The Klyn test framework discovers packaged test classes and executes the complete selection in one isolated test process and one JIT session. It reports class and method results incrementally and can persist the complete execution as JSON. The same tests/Start.kn entry point supports console and graphical runs.

Minimal Test Starter

A project needs only one small entry point. Pass arguments unchanged to TestLauncher.run(); the launcher handles package filters, console reporting, JSON output, help, and graphical mode. Its return value is the process exit code.

# tests/Start.kn
import klyn.unittest.launcher

exit(TestLauncher.run(arguments))

Keeping option handling in the framework guarantees that every project exposes the same command line and that new framework options do not require changes to its starter.

One process for the complete selection

All selected classes share one test process and one JIT session. This avoids repeating runtime, compiler, and standard-library initialization for every class while retaining a distinct JIT namespace and a structured result for each source. A fatal native crash can stop the remaining classes; the runner reports every source that could not execute as an infrastructure failure.

Test Source Layout

The tests directory is the class-loader root. Every test package must therefore match its directory, exactly as it does for application sources. Discovery is driven by @TestClass, not by a filename convention; ending test filenames with Test.kn remains the recommended convention.

tests/
|-- Start.kn
`-- inventory/
    |-- InventoryServiceTest.kn
    `-- persistence/
        `-- InventoryRepositoryTest.kn
# tests/inventory/InventoryServiceTest.kn
package inventory

import klyn.unittest

@TestClass
public class InventoryServiceTest:

    @Test
    public testAvailableQuantity():
        assert 12 - 4 == 8
Packages are validated

Declaring package inventory.persistence in tests/inventory/InventoryServiceTest.kn is an error. Move the file or correct its package; the runner never hides a package-layout problem.

Test and Fixture Annotations
Annotation Execution point
@TestClass Marks the public class containing test methods.
@BeforeClass Runs once before the first test method.
@Before Runs before every test method.
@Test Marks one independently reported test method.
@After Runs after every test method.
@AfterClass Runs once after the last test method.
package inventory

import klyn.unittest

@TestClass
public class InventoryRepositoryTest:

    private _quantity as Int

    @BeforeClass
    public static prepareDatabase():
        print("Preparing test data")

    @Before
    public resetQuantity():
        this._quantity = 10

    @Test
    public testRemoval():
        this._quantity -= 3
        assert this._quantity == 7

    @After
    public verifyInvariant():
        assert this._quantity >= 0

    @AfterClass
    public static closeDatabase():
        print("Releasing test data")
Expected Errors and Timeouts

Use expected when an exception is the successful outcome of a test. A @Test(timeout=...) budget limits one test method, while @TestClass(timeout=...) limits the complete lifecycle of a test class. Both values are expressed in milliseconds; zero, the default, disables the corresponding limit.

@Test(expected=ValueException)
public testRejectsNegativeQuantity():
    throw ValueException("Quantity cannot be negative")

@Test(timeout=500)
public testCompletesQuickly():
    value = 40 + 2
    assert value == 42

@TestClass(timeout=2000)
public class FastContractTest:

    @Test
    public completesWithinTheClassBudget():
        assert true

A timed-out method is interrupted and reported as failed. It cannot satisfy an expected declaration. A class budget includes construction, @BeforeClass, @Before, every @Test, @After and @AfterClass; source compilation is intentionally excluded. When method and class budgets are both present, the first deadline reached stops execution.

Running Tests
# Run every @TestClass below tests
klyn tests/Start.kn

# Run one package and all of its subpackages
klyn tests/Start.kn klyn.data

# Combine any number of package roots
klyn tests/Start.kn klyn.data klyn.databases

# Display every supported option
klyn tests/Start.kn --help

Each selected source runs in its own process. A native crash, compilation failure, or leaked global state in one test class therefore cannot corrupt the next class. Successful entries and the final successful summary are green; failures are red and identify the affected test methods. Each class occupies one console line; OK or KO is aligned against the right edge, and long source names are shortened in the middle without hiding the test filename.

Running /project/tests/klyn/regex/RegExTest.kn...               0.042 s OK
Test summary: 1/1 test class(es) passed; 8/8 test method(s) passed; 0.042 s.
JSON Reports
# Default destination: test-results.json
klyn tests/Start.kn --json

# Explicit destination
klyn tests/Start.kn --json=reports/collections.json klyn.collections

The report records duration, source, package, class and method status, captured standard output and error streams, stack traces, and infrastructure failures. It can be loaded later by the graphical runner.

Graphical Test Runner
klyn tests/Start.kn --gui
klyn tests/Start.kn --gui klyn.io klyn.regex

The GUI uses DockWorkspace. Its tree groups packages, classes, and methods and displays a red or green status dot at the right of each row. The root stays expanded; passing branches start collapsed, while every branch containing a failure starts expanded. Selecting a package or class aggregates its details in the central area. The window opens before test execution starts; tests run in an isolated worker and completed classes are published to the tree, output, and summary in real time without blocking GUI painting or input.

  • Output preserves the complete nominal and error streams.
  • Details focuses on the selected package, class, or method.
  • File > Save report persists the current report as JSON.
  • File > Load report opens a previous JSON execution.

Save and load actions use the embedded SaveFileLightBox and OpenFileLightBox from klyn.gui.windows.dialogs. They remain inside the test-runner window and do not start a nested event loop.

One entry point

Graphical execution is not a separate program. Always use klyn tests/Start.kn --gui, so discovery, filtering, isolation, and result semantics remain identical to console mode.

IDE Integration

The Klyn extension uses the native VS Code Testing API. Starting a test from the editor, gutter, or Explorer reveals the Tests view and result panel immediately. Packages, classes, and methods remain hierarchical; selecting any node displays the output, duration, and errors aggregated for that node.

Internally, --json-stream publishes framed JSON Lines events whenever a class starts or completes. This lets IDEs update their test tree in real time while the regular --json report remains the authoritative final snapshot and fallback.

Programmatic Execution

Build tools can use TestRunner and TestReport directly. Package filters retain the same exact-or-descendant semantics as the command line.

import klyn.io
import klyn.unittest

runner = TestRunner(FolderPath("tests"))
runner.consoleOutput = false

report = runner.run(["klyn.collections"])
report.save(FilePath("collection-results.json"))

if not report.passed:
    Application.exit(Application.EXIT_FAILURE)