How do I test that code throws or does not throw exceptions in EK9?

← Testing · Ref: Q209

EK9 provides try/catch for testing exception behavior, plus assertThrows and assertDoesNotThrow keywords for direct exception testing.

TRY/CATCH FOR EXCEPTION TESTING

Use try/catch to verify an exception is thrown:

  try
    riskyFunction()
    assert false  // Should not reach here
  catch
    -> ex as Exception
    assert ex?
    stdout.println("Caught: " + $ex)

ASSERTTHROWS

Verify that a block throws an exception:

  assertThrows
    riskyFunction()

The test passes only if an exception is thrown.

ASSERTDOESNOTTHROW

Verify that a block completes without throwing:

  assertDoesNotThrow
    safeFunction()

The test fails if any exception is thrown.

TESTING EXCEPTION MESSAGES

Catch and inspect exception details:

  try
    throwError()
  catch
    -> ex as Exception
    assert $ex == "expected message"

See Q134 for try/catch basics. See Q136 for throwing exceptions. See Q156 for assertions.

See Q305 for require vs assert vs throw. See Q306 for assertThrows in depth. See Q307 for assertDoesNotThrow in depth.

Example

defines module qa.testdeep.exceptions

  defines function

    riskyDivide() as pure
      ->
        a as Integer
        b as Integer
      <-
        rtn as Integer?

      require b?
      rtn: a / b

  defines program

    ExceptionTestingDemo()
      stdout <- Stdout()

      // === TRY/CATCH FOR EXCEPTION TESTING ===

      try
        result <- riskyDivide(10, 2)
        stdout.println(`10 / 2 = ${result}`)
      catch
        -> ex as Exception
        stdout.println("Unexpected error: " + $ex)

      // === TESTING ERROR CONDITIONS ===

      try
        discarded <- riskyDivide(10, 0)
        stdout.println("Should not reach here " + $discarded)
      catch
        -> ex as Exception
        stdout.println("Caught expected error: " + $ex)

      // === ASSERTTHROWS / ASSERTDOESNOTTHROW ===
      // In @Test programs:
      //   assertThrows
      //     riskyDivide(10, 0)
      //   assertDoesNotThrow
      //     riskyDivide(10, 2)

      stdout.println("assertThrows verifies exception is thrown")
      stdout.println("assertDoesNotThrow verifies no exception")

Common mistakes

E08091 — Both parameters a and b are declared but neither would be used in the function body if the implementation ignores them. All parameters must be referenced. See ek9 -h E08091 for details.

Incorrect:

riskyDivide() as pure
      ->
        a as Integer
        b as Integer
      <-
        rtn as Integer?

      rtn: 0

Correct:

    riskyDivide() as pure
      ->
        a as Integer
        b as Integer
      <-
        rtn as Integer?

      require b?
      rtn: a / b
Other ways to ask this
  • How do I verify exceptions in EK9 tests?
  • How do assertThrows and assertDoesNotThrow work in EK9?
  • How do I test error handling in EK9?

Coming from another language?

Java: JUnit assertThrows(() -> code) and assertDoesNotThrow(). Python: pytest.raises(ExceptionType). Rust: #[should_panic] attribute. Go: custom helper checking error returns. Kotlin: assertThrows<Exception> { code }. EK9: assertThrows/assertDoesNotThrow keywords, plus try/catch for detailed inspection.

Keywords: catch, coverage, assertDoesNotThrow, assertThrows, test, verify, error, exception, throw