How do I test that my program exits with a particular exit code?

← Testing · Ref: Q1540

Declare it on the @Test directive with "EXIT:<n>", where n is a whole number from 0 to 255.

A program's returned Integer is its exit code, and under 'ek9 -t' the test judges it: a test passes only when its program exits with exactly the code the test expects. Without an EXIT part that is 0, so a test program that returns 3 FAILS with 'Test program exited with code 3'. Declare the 3 and the same program passes:

  @Test: "EXIT:3"
  EmptyOrderIsRejected()
    <- rtn as Integer: exitCodeFor(noOrder)
    assert rtn?

The declared code is checked both ways: a test declaring "EXIT:3" whose program exits 0 or 4 also fails ('Test program exited with code 0 but its @Test declares "EXIT:3"').

IT MUST BE THE PROGRAM'S OWN CODE

Return the code from the program. An uncaught Exception or Panic also ends the program with a non-zero code, but it writes to stderr as it does, and the test runner reports that as an error, never as the expected exit.

COMBINING WITH A REQUIREMENT AND A GROUP

A @Test carries up to three parts, in any order: a requirement id, an execution group and the exit code.

  @Test: "REQ:input-validation": "validation": "EXIT:3"

Each part is told apart by what it contains, so a mistake is a compile error rather than a silently ignored expectation: "EXIT:abc", "EXIT:", "EXIT:256", a second EXIT part, and a misspelled prefix such as "EXT:3" (it contains a colon, so it cannot quietly become a group name) are all rejected with E50200.

See Q1437 for how a program's exit code differs from the ek9 command's. See Q806 for test groups. See Q155 for writing tests.

Example

defines module qa.testdeep.expectedexitcode

  defines function

    exitCodeFor() as pure
      -> order as String
      <- rtn as Integer: 0
      if order is empty
        rtn: 3

  defines constant
    noOrder <- ""

  defines program

    //Returns 3 for an empty order, and declares that 3 is what it must exit with.
    @Test: "EXIT:3"
    EmptyOrderIsRejected()
      <- rtn as Integer: exitCodeFor(noOrder)
      assert rtn?

Common mistakes

E50200 — The expected exit code is written EXIT:<n>. A misspelled prefix contains a colon, so it is rejected rather than quietly becoming an execution group and dropping the expectation. See ek9 -h E50200 for details.

Incorrect:

    @Test: "EXT:3"

Correct:

    @Test: "EXIT:3"
Other ways to ask this
  • What does @Test: "EXIT:3" do in EK9?
  • My @Test program returns 3 and the test fails - how do I say that 3 is expected?
  • How do I declare the expected exit code of an EK9 test?
  • Can a test check a non-zero exit status in EK9?

Coming from another language?

Java: JUnit has no declared exit status; testing System.exit needs a SecurityManager workaround or a subprocess and a hand-written assertion. Python: pytest.raises(SystemExit) and then checking .code. Go: run the binary with os/exec and inspect ExitCode(). EK9: the expected exit code is part of the @Test directive and is checked when the source compiles.

Keywords: exit code, test, @Test, exit status, non-zero exit, expected exit code, returned Integer, EXIT