Module unittest

Author: Zahary Karadjov

This module implements boilerplate to make unit testing easy.

Example:

suite "description for this stuff":
  test "essential truths":
    # give up and stop if this fails
    require(true)
  
  test "slightly less obvious stuff":
    # print a nasty message and move on, skipping
    # the remainder of this block
    check(1 != 1)
    check("asd"[2] == 'd')
  
  test "out of bounds error is thrown on bad access":
    let v = @[1, 2, 3]  # you can do initialization here
    expect(IndexError):
      discard v[4]

Types

TestStatus = enum
  OK, FAILED, SKIPPED
The status of a test when it is done.   Source
OutputLevel = enum
  PRINT_ALL,                  ## Print as much as possible.
  PRINT_FAILURES,             ## Print only the failed tests.
  PRINT_NONE                  ## Print nothing.
The output verbosity of the tests.   Source

Vars

abortOnError: bool
Set to true in order to quit immediately on fail. Default is false, unless the NIMTEST_ABORT_ON_ERROR environment variable is set for the non-js target.   Source
outputLevel: OutputLevel
Set the verbosity of test results. Default is PRINT_ALL, unless the NIMTEST_OUTPUT_LVL environment variable is set for the non-js target.   Source
colorOutput: bool
Have test results printed in color. Default is true for the non-js target unless, the environment variable NIMTEST_NO_COLOR is set.   Source

Procs

proc checkpoint(msg: string) {.raises: [], tags: [].}
Set a checkpoint identified by msg. Upon test failure all checkpoints encountered so far are printed out. Example:
checkpoint("Checkpoint A")
check((42, "the Answer to life and everything") == (1, "a"))
checkpoint("Checkpoint B")

outputs "Checkpoint A" once it fails.

  Source

Macros

macro check(conditions: stmt): stmt {.immediate.}
Verify if a statement or a list of statements is true. A helpful error message and set checkpoints are printed out on failure (if outputLevel is not PRINT_NONE). Example:
import strutils

check("AKB48".toLower() == "akb48")

let teams = {'A', 'K', 'B', '4', '8'}

check:
  "AKB48".toLower() == "akb48"
  'C' in teams
  Source
macro expect(exceptions: varargs[expr]; body: stmt): stmt {.immediate.}
Test if body raises an exception found in the passed exceptions. The test passes if the raised exception is part of the acceptable exceptions. Otherwise, it fails. Example:
import math, random
proc defectiveRobot() =
  randomize()
  case random(1..4)
  of 1: raise newException(OSError, "CANNOT COMPUTE!")
  of 2: discard parseInt("Hello World!")
  of 3: raise newException(IOError, "I can't do that Dave.")
  else: assert 2 + 2 == 5

expect IOError, OSError, ValueError, AssertionError:
  defectiveRobot()
  Source

Templates

template suite(name: expr; body: stmt): stmt {.immediate, dirty.}

Declare a test suite identified by name with optional setup and/or teardown section.

A test suite is a series of one or more related tests sharing a common fixture (setup, teardown). The fixture is executed for EACH test.

suite "test suite for addition":
  setup:
    let result = 4
  
  test "2 + 2 = 4":
    check(2+2 == result)
  
  test "(2 + -2) != 4":
    check(2 + -2 != result)
  
  # No teardown needed

The suite will run the individual test cases in the order in which they were listed. With default global settings the above code prints:

[OK] 2 + 2 = 4
[OK] (2 + -2) != 4
  Source
template test(name: expr; body: stmt): stmt {.immediate, dirty.}
Define a single test case identified by name.
test "roses are red":
  let roses = "red"
  check(roses == "red")

The above code outputs:

[OK] roses are red
  Source
template fail()
Print out the checkpoints encountered so far and quit if abortOnError is true. Otherwise, erase the checkpoints and indicate the test has failed (change exit code and test status). This template is useful for debugging, but is otherwise mostly used internally. Example:
checkpoint("Checkpoint A")
complicatedProcInThread()
fail()

outputs "Checkpoint A" before quitting.

  Source
template skip()
Makes test to be skipped. Should be used directly in case when it is not possible to perform test for reasons depending on outer environment, or certain application logic conditions or configurations.
if not isGLConextCreated():
  skip()
  Source
template require(conditions: stmt): stmt {.immediate.}
Same as check except any failed test causes the program to quit immediately. Any teardown statements are not executed and the failed test output is not generated.   Source