A very small testing tool

Here’s a very small unit testing framework that I whopped up in a few minutes, in case it’s useful to anyone. Test cases are declared with (test $) and by inclusion in (list of tests []), and pass if they succeed and fail if they fail.

%% testrunner.dg
(extension version)	Unit test runner v0.1 by Susan Davis (line)

%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])
(interface (test $<Testname))

(all tests pass)

(program entry point)
	(list of tests $Tests)
	(length of $Tests into $Number)
	(bold) Attempting $Number unit tests (roman) (line)
	(stoppable) (exhaust) {
		*($Test is one of $Tests)
		Testing $Test: 
		(run-test $Test)
	}
	(if) (all tests pass) (then)
		(bold) $Number tests passed successfully. (roman) (par)
	(else)
		(bold) FAILED TEST. (roman) (par)
	(endif)

(error $Code entry point)
	(now) ~(all tests pass)
	(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
	(stop)

(run-test $Test)
	(if) 
		(test $Test)
	(then)
		Passed! (line)
	(else)
		Failed. (space) :-\( (line)
		(now) ~(all tests pass)
		(stop)
	(endif)
5 Likes

One small change:

%% testrunner.dg
(extension version)	Unit test runner v0.2 by Susan Davis (line)

%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])
(interface (test $<Testname))

(interface (clean up $<Testname))

(global variable (list of tests $))

(all tests pass)

(program entry point)
	(list of tests $Tests)
	(length of $Tests into $Number)
	(bold) Attempting $Number unit tests (roman) (line)
	(stoppable) (exhaust) {
		*($Test is one of $Tests)
		Testing $Test: 
		(run-test $Test)
	}
	(if) (all tests pass) (then)
		(bold) $Number tests passed successfully. (roman) (par)
	(else)
		(bold) FAILED TEST. (roman) (par)
	(endif)

(error $Code entry point)
	(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
	(now) ~(all tests pass)
	(stop)

(run-test $Test)
	(if) 
		(test $Test)
		(clean up $Test)
	(then)
		Passed! (line)
	(else)
		Failed. (space) :-\( (line)
		(now) ~(all tests pass)
		(stop)
	(endif)

One more obvious change:

%% testrunner.dg
(extension version)	Unit test runner v0.3 by Susan Davis (line)

%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])

(interface (test $<Testname))

(interface (set up $<Testname))

(set up $)

(interface (clean up $<Testname))

(clean up $)

(global variable (list of tests $))

(all tests pass)

(program entry point)
	(list of tests $Tests)
	(length of $Tests into $Number)
	(bold) Attempting $Number unit tests (roman) (line)
	(stoppable) (exhaust) {
		*($Test is one of $Tests)
		Testing $Test: 
		(run-test $Test)
	}
	(if) (all tests pass) (then)
		(bold) $Number tests passed successfully. (roman) (par)
	(else)
		(bold) FAILED TEST. (roman) (par)
	(endif)

(error $Code entry point)
	(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
	(now) ~(all tests pass)
	(stop)

(run-test $Test)
	(if)
		(set up $Test)
		(test $Test)
		(clean up $Test)
	(then)
		Passed! (line)
	(else)
		Failed. (space) :-\( (line)
		(now) ~(all tests pass)
		(stop)
	(endif)

If this grows much beyond 1k or so, I’ll find a place on Github for it to live.

Exit with an error status code if applicable, if @lft adds support for exiting the debugger with a specified status someday.

%% testrunner.dg
(extension version)	Unit test runner v0.4 by Susan Davis (line)

%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])

(interface (test $<Testname))

(interface (set up $<Testname)) %% special setup for the test

(set up $) %% don't fail if no setup provided

(interface (clean up $<Testname)) %% restore to original state afterward

(clean up $) %% don't fail if no cleanup provided

(global variable (list of tests $))

(all tests pass)

(program entry point)
	(list of tests $Tests)
	(length of $Tests into $Number)
	(bold) Attempting $Number unit tests (roman) (line)
	(stoppable) (exhaust) {
		*($Test is one of $Tests)
		Testing $Test: 
		(run-test $Test)
	}
	(if) (all tests pass) (then)
		(bold) $Number tests passed successfully. (roman) (par)
		(quit 0)
	(else)
		(bold) FAILED TEST. (roman) (par)
		(quit 1)
	(endif)

(error $Code entry point)
	(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
	(now) ~(all tests pass)  
	(quit 1)                 %% or -1 if we ever get signed integers

(run-test $Test)
	(if)
		(set up $Test)
		(test $Test)
		(clean up $Test)
	(then)
		Passed! (line)
	(else)
		Failed. (space) :-\( (line)
		(now) ~(all tests pass)
		(stop)
	(endif)

%% Delete this interface and predicate if lft adds a builtin for it in
%% dgdebug, to specify the exit status from the debugger for CI/CD pipelines
%% and/or shell scripts.
(interface (quit $<ExitStatus))
(quit $)   (quit)

Added a patch from @ifste to fix some behaviour in ($ contains sublist $)

%% testrunner.dg
(extension version)	Unit test runner v0.5 by Susan Davis (line)

%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])

(interface (test $<Testname))

(interface (set up $<Testname)) %% special setup for the test

(interface (clean up $<Testname)) %% restore to original state afterward

(interface (assert $<Var = $Val))

(assert $Thing1 = $Thing2) %% true if both are bound and ($Thing1 = $Thing2)
	(fully bound $Thing1)
	(bound $Thing2)
	($Thing1 = $Thing2)

(global variable (list of tests $))

(all tests pass)

(program entry point)
	(list of tests $Tests)
	(length of $Tests into $Number)
	(bold) Attempting $Number unit tests (roman) (line)
	(stoppable) (exhaust) {
		*($Test is one of $Tests)
		Testing $Test: 
		(run-test $Test)
	}
	(if) (all tests pass) (then)
		(bold) $Number tests passed successfully. (roman) (par)
		(quit 0)
	(else)
		(bold) FAILED TEST. (roman) (par)
		(quit 1)
	(endif)

(error $Code entry point)
	(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
	(now) ~(all tests pass)  
	(quit 1)                 %% or -1 if we ever get signed integers

(run-test $Test)
	(if)
		(stoppable) (set up $Test)   %% don't fail if absent
		(test $Test)
		(stoppable) (clean up $Test) %% don't fail if absent
	(then)
		Passed! (line)
	(else)
		Failed. (space) :-\( (line)
		(now) ~(all tests pass)
		(stop)
	(endif)

%% patch for ($ contains sublist $) issues; delete when fixed
($List contains sublist [$Head | $Tail])
	*(split $List by [$Head] into $ and $Rest)
	(append $Tail $ $Rest) 

%% Delete this interface and predicate if lft adds a builtin for it in
%% dgdebug, to specify the exit status from the debugger for CI/CD pipelines
%% and/or shell scripts.
(interface (quit $<ExitStatus))
(quit $)   (quit)
1 Like