Added docs to einheit.nim and more examples to test.nim.
This commit is contained in:
parent
24be4aeef9
commit
6a9ec2f40a
2 changed files with 109 additions and 9 deletions
72
einheit.nim
72
einheit.nim
|
|
@ -14,35 +14,43 @@
|
||||||
## self.assert_false(3 == 4)
|
## self.assert_false(3 == 4)
|
||||||
## self.assert_raises(OSError, newException(OSError, "OS is exploding!"))
|
## self.assert_raises(OSError, newException(OSError, "OS is exploding!"))
|
||||||
##
|
##
|
||||||
|
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
import strutils
|
import strutils
|
||||||
when not defined(ECMAScript):
|
when not defined(ECMAScript):
|
||||||
import terminal
|
import terminal
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TestSuite = ref object of RootObj
|
TestSuite = ref object of RootObj
|
||||||
|
## The base TestSuite
|
||||||
name: string
|
name: string
|
||||||
current_test_name: string
|
current_test_name: string
|
||||||
tests_passed: int
|
tests_passed: int
|
||||||
num_tests: int
|
num_tests: int
|
||||||
|
|
||||||
TestAssertError = object of Exception
|
TestAssertError = object of Exception
|
||||||
|
## assert_true and other assert_* statements will raise
|
||||||
|
## this exception when the condition fails
|
||||||
line_number: int
|
line_number: int
|
||||||
file_name: string
|
file_name: string
|
||||||
code_snip: string
|
code_snip: string
|
||||||
test_name: string
|
test_name: string
|
||||||
|
|
||||||
# Methods for the TestSuite base
|
# -- Methods for the TestSuite base --
|
||||||
|
|
||||||
method setup*(suite: TestSuite)=
|
method setup*(suite: TestSuite)=
|
||||||
|
## Base method for setup code
|
||||||
discard
|
discard
|
||||||
|
|
||||||
method run_tests*(suite: TestSuite)=
|
method run_tests*(suite: TestSuite)=
|
||||||
|
## Base method for running tests
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
# ------------------------------------
|
||||||
|
|
||||||
|
|
||||||
template return_exception(name, test_name, snip, vals)=
|
template return_exception(name, test_name, snip, vals)=
|
||||||
|
## private template for raising an exception
|
||||||
let pos = instantiationInfo(fullpaths=true)
|
let pos = instantiationInfo(fullpaths=true)
|
||||||
let pos_rel = instantiationInfo()
|
let pos_rel = instantiationInfo()
|
||||||
var
|
var
|
||||||
|
|
@ -60,7 +68,10 @@ template return_exception(name, test_name, snip, vals)=
|
||||||
exc.test_name = test_name
|
exc.test_name = test_name
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
|
# --------------------------- Templates for assertion --------------------------------------
|
||||||
|
|
||||||
template assert_equal*(self: TestSuite, lhs: untyped, rhs: untyped): untyped {.immediate.}=
|
template assert_equal*(self: TestSuite, lhs: untyped, rhs: untyped): untyped {.immediate.}=
|
||||||
|
## Raises a TestAssertError when the lhs and the rhs are not equal.
|
||||||
if (lhs) != (rhs):
|
if (lhs) != (rhs):
|
||||||
var snip = astToStr(lhs) & ", " & astToStr(rhs)
|
var snip = astToStr(lhs) & ", " & astToStr(rhs)
|
||||||
|
|
||||||
|
|
@ -71,6 +82,7 @@ template assert_equal*(self: TestSuite, lhs: untyped, rhs: untyped): untyped {.i
|
||||||
return_exception("assert_true", test_name, snip, vals)
|
return_exception("assert_true", test_name, snip, vals)
|
||||||
|
|
||||||
template assert_true*(self: TestSuite, code: untyped): untyped {.immediate.}=
|
template assert_true*(self: TestSuite, code: untyped): untyped {.immediate.}=
|
||||||
|
## Raises a TestAssertError when the code is false.
|
||||||
if not code:
|
if not code:
|
||||||
var snip = astToStr(code)
|
var snip = astToStr(code)
|
||||||
|
|
||||||
|
|
@ -81,6 +93,7 @@ template assert_true*(self: TestSuite, code: untyped): untyped {.immediate.}=
|
||||||
return_exception("assert_true", test_name, snip, vals)
|
return_exception("assert_true", test_name, snip, vals)
|
||||||
|
|
||||||
template assert_false*(self: TestSuite, code: untyped): untyped {.immediate.}=
|
template assert_false*(self: TestSuite, code: untyped): untyped {.immediate.}=
|
||||||
|
## Raises a TestAssertError when the code is true.
|
||||||
if code:
|
if code:
|
||||||
var snip = astToStr(code)
|
var snip = astToStr(code)
|
||||||
|
|
||||||
|
|
@ -92,6 +105,8 @@ template assert_false*(self: TestSuite, code: untyped): untyped {.immediate.}=
|
||||||
|
|
||||||
|
|
||||||
template assert_raises*(self: TestSuite, error: untyped, code: untyped): untyped {.immediate.}=
|
template assert_raises*(self: TestSuite, error: untyped, code: untyped): untyped {.immediate.}=
|
||||||
|
## Raises a TestAssertError when the exception "error" is
|
||||||
|
## not thrown in the code
|
||||||
try:
|
try:
|
||||||
code
|
code
|
||||||
var
|
var
|
||||||
|
|
@ -115,10 +130,53 @@ template assert_raises*(self: TestSuite, error: untyped, code: untyped): untyped
|
||||||
|
|
||||||
discard
|
discard
|
||||||
|
|
||||||
# A list to hold all test suites
|
# --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# A list to hold all test suites that are created
|
||||||
var test_suites: seq[TestSuite] = @[]
|
var test_suites: seq[TestSuite] = @[]
|
||||||
|
|
||||||
macro test_suite*(head: untyped, body: untyped): untyped =
|
macro test_suite*(head: untyped, body: untyped): untyped =
|
||||||
|
## Compile-time macro that allows a user to define tests and run them
|
||||||
|
##
|
||||||
|
## Methods are used for inheritance, so if you want to derive a test
|
||||||
|
## suite, then you have to make sure the base suite uses methods
|
||||||
|
## for the tests that you want to derive.
|
||||||
|
##
|
||||||
|
## If you don't want inheritance, you can just use procs.
|
||||||
|
##
|
||||||
|
## A special proc/method is called setup(). The macro will inject
|
||||||
|
## this if it doesn't exist and it will be called before running
|
||||||
|
## the test suite.
|
||||||
|
##
|
||||||
|
## Test methods/procs to be run are prefixed with "test" in the
|
||||||
|
## method/proc name. This is so that you can write tests that call
|
||||||
|
## procs that do other things and won't be run as a test.
|
||||||
|
##
|
||||||
|
## For each suite method/proc, an implicit variable called "self"
|
||||||
|
## is added. This lets you access the test_suite in an OO kind
|
||||||
|
## of way.
|
||||||
|
##
|
||||||
|
## Usage:
|
||||||
|
##
|
||||||
|
## .. code:: nim
|
||||||
|
##
|
||||||
|
## test_suite SuiteName of TestSuite:
|
||||||
|
##
|
||||||
|
## var
|
||||||
|
## suite_var: string
|
||||||
|
##
|
||||||
|
## method setup()=
|
||||||
|
## ## do setup code here
|
||||||
|
## self.suite_var = "Testing"
|
||||||
|
##
|
||||||
|
## method test_adding_string()=
|
||||||
|
## ## adds a string to the suite_var
|
||||||
|
## self.suite_var &= " 123"
|
||||||
|
## self.assert_equal(self.suite_var, "Testing 123")
|
||||||
|
##
|
||||||
|
## when isMainModule:
|
||||||
|
## einheit.run_tests()
|
||||||
|
|
||||||
# object reference name inside methods.
|
# object reference name inside methods.
|
||||||
# ie: self, self
|
# ie: self, self
|
||||||
|
|
@ -314,7 +372,7 @@ macro test_suite*(head: untyped, body: untyped): untyped =
|
||||||
let dot_name = newDotExpr(ident(obj_reference), ident("name"))
|
let dot_name = newDotExpr(ident(obj_reference), ident("name"))
|
||||||
let set_name = newAssignment(dot_name, newLit(typeName))
|
let set_name = newAssignment(dot_name, newLit(typeName))
|
||||||
n2.body.add(set_name)
|
n2.body.add(set_name)
|
||||||
else:
|
elif proc_name.startswith("test"):
|
||||||
let proc_call = newDotExpr(ident(obj_reference),
|
let proc_call = newDotExpr(ident(obj_reference),
|
||||||
ident(proc_name & type_name))
|
ident(proc_name & type_name))
|
||||||
|
|
||||||
|
|
@ -428,7 +486,11 @@ macro test_suite*(head: untyped, body: untyped): untyped =
|
||||||
|
|
||||||
result.add(getAst(add_test_suite(typeName)))
|
result.add(getAst(add_test_suite(typeName)))
|
||||||
|
|
||||||
|
|
||||||
proc run_tests*()=
|
proc run_tests*()=
|
||||||
|
## The method that runs the tests. Invoke
|
||||||
|
## after setting up all of the tests and
|
||||||
|
## usually inside a "when isMainModule" block
|
||||||
for suite in test_suites:
|
for suite in test_suites:
|
||||||
suite.setup()
|
suite.setup()
|
||||||
|
|
||||||
|
|
|
||||||
46
test.nim
46
test.nim
|
|
@ -4,16 +4,23 @@ test_suite UnitTests:
|
||||||
var
|
var
|
||||||
test_obj: int
|
test_obj: int
|
||||||
|
|
||||||
|
proc do_things()=
|
||||||
|
# This proc won't be invoked as a test
|
||||||
|
self.test_obj = 400
|
||||||
|
self.assert_true(self.test_obj == 400)
|
||||||
|
|
||||||
method setup()=
|
method setup()=
|
||||||
self.test_obj = 5
|
self.test_obj = 5
|
||||||
self.test_obj = 90
|
self.test_obj = 90
|
||||||
|
|
||||||
method test_for_b()=
|
method test_for_b()=
|
||||||
var b = 0
|
var b = 0
|
||||||
|
self.do_things()
|
||||||
self.assert_true(4 == 4)
|
self.assert_true(4 == 4)
|
||||||
|
|
||||||
method test_for_c()=
|
method test_for_c()=
|
||||||
var c = 0
|
var c = 0
|
||||||
|
# supposed to fail
|
||||||
self.assert_equal(c, 1)
|
self.assert_equal(c, 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,18 +45,49 @@ test_suite UnitTestsNew:
|
||||||
self.assert_false("String" != "String")
|
self.assert_false("String" != "String")
|
||||||
|
|
||||||
|
|
||||||
proc raises_os()=
|
|
||||||
raise newException(OSError, "Oh no! OS malfunction!")
|
|
||||||
|
|
||||||
# Inheritance!
|
# Inheritance!
|
||||||
test_suite TestInherit of UnitTestsNew:
|
test_suite TestInherit of UnitTestsNew:
|
||||||
|
## This will call every test defined in UnitTestsNew
|
||||||
|
|
||||||
|
proc raises_os()=
|
||||||
|
# This proc won't be invoked as a test
|
||||||
|
raise newException(OSError, "Oh no! OS malfunction!")
|
||||||
|
|
||||||
method test_raises()=
|
method test_raises()=
|
||||||
|
|
||||||
# Two ways of asserting
|
# Two ways of asserting
|
||||||
self.assert_raises OSError:
|
self.assert_raises OSError:
|
||||||
raises_os()
|
self.raises_os()
|
||||||
|
|
||||||
|
self.assert_raises(OSError, self.raises_os())
|
||||||
|
|
||||||
|
|
||||||
|
test_suite MoreInheritance of TestInherit:
|
||||||
|
|
||||||
|
method setup()=
|
||||||
|
# This must be called if overriding setup if you want
|
||||||
|
# base class setup functionality. You can also call
|
||||||
|
# self.setupTestInherit() to call the direct parent's
|
||||||
|
# implementation
|
||||||
|
self.setupUnitTestsNew()
|
||||||
|
|
||||||
|
# This will make one of the tests inherited from UnitTestsNew
|
||||||
|
# fail. This is expected.
|
||||||
|
self.test_obj = 12345
|
||||||
|
|
||||||
|
method test_test_obj()=
|
||||||
|
# This method is overwritten. To call the base method,
|
||||||
|
# simply use
|
||||||
|
# self.test_test_obj_UnitTestsNew()
|
||||||
|
# However, currently this method will be run
|
||||||
|
# IN ADDITION to the base class's method.
|
||||||
|
# This one will pass, the other will fail
|
||||||
|
self.assert_true(self.test_obj == 12345)
|
||||||
|
|
||||||
|
method test_new_obj()=
|
||||||
|
self.assert_equal(self.test_obj, 12345)
|
||||||
|
|
||||||
self.assert_raises(OSError, raises_os())
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
run_tests()
|
run_tests()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue