How do I write a unit test in EK9?

← Testing · Ref: Q155

EK9 has built-in testing support with no external framework needed. Mark any program with @Test to make it a test. Group related tests with @Test: "groupName".

THE @TEST DIRECTIVE

Place @Test on its own line before a program to mark it as a test:

  defines program
    @Test
    SimpleTest()
      stdout <- Stdout()
      stdout.println("Test passed")

The @Test annotation must be on its own line, not inline.

TEST PROGRAMS

Tests are just programs. They run in the same way as normal programs but are collected and executed by the test runner. Any program marked @Test is included when you run ek9 -t file.ek9.

TEST GROUPS

Group related tests with @Test: "groupName":

  @Test: "database"
  DbConnectionTest()
    // test database connection
  @Test: "database"
  DbQueryTest()
    // test queries

Run a specific group with ek9 -tg database file.ek9. Tests in the same group run sequentially.

BLACK BOX TESTS

For output-based testing, print expected values to stdout and compare against an expected_output.txt file. This avoids assert bytecode contamination and provides clean test verification.

NO EXTERNAL FRAMEWORK NEEDED

Unlike Java (JUnit), Python (pytest), or Rust (#[test] with cargo), EK9 testing is built into the language. No dependencies to add, no test runner to install.

See Q156 for assertions. See Q157 for running tests. See Q2 for compile and run. See Q204 for black-box testing. See Q209 for exception testing.

Example

defines module qa.testing.basics

  defines program

    // === BASIC TEST ===

    @Test
    SimpleArithmeticTest()
      stdout <- Stdout()
      result <- 2 + 3
      assert result == 5
      stdout.println("2 + 3 = " + $result)

    // === GROUPED TESTS ===

    @Test: "strings"
    StringConcatenationTest()
      stdout <- Stdout()
      full <- `Hello World`
      assert full == "Hello World"
      stdout.println("Concatenation: " + full)

    @Test: "strings"
    StringLengthTest()
      stdout <- Stdout()
      text <- "EK9"
      assert text.length() == 3
      stdout.println("Length: " + $text.length())

    // === TEST WITH SETUP ===

    @Test
    ListOperationsTest()
      stdout <- Stdout()
      items <- List() of Integer
      items += 10
      items += 20
      items += 30
      assert items.length() == 3
      stdout.println("List size: " + $items.length())

Common mistakes

E50060 — Integer has no toString() method. Use the $ prefix operator for string conversion or string interpolation. See ek9 -h E50060 for details.

Incorrect:

stdout.println(result.toString())

Correct:

stdout.println("2 + 3 = " + $result)

E50060 — String has no size() method. Use length() to get the number of characters in a string. See ek9 -h E50060 for details.

Incorrect:

stdout.println("Length: " + $text.size())

Correct:

stdout.println("Length: " + $text.length())
Other ways to ask this
  • How do I create tests in EK9?
  • What is the @Test directive in EK9?
  • Does EK9 have a built-in test framework?

Coming from another language?

Java: JUnit @Test annotation with separate framework dependency. Python: pytest or unittest with class-based test structure. Rust: #[test] attribute with cargo test runner. Go: func TestXxx(t *testing.T) naming convention. EK9: built-in @Test directive on programs, no framework dependency, optional grouping with @Test: "group".

Keywords: framework, migrate, program, unit, test, directive, assert, testing, built-in, group, verify