# a minimal test runner the following class executes test cases were input arguments and expected output can be specified in a compact format. it is a minimalistic coffeescript, and therefore also javascript, library for automatic testing. example ~~~ tests = [ [ string_indices [["/a////b//c/d","/"]] # list of input arguments [0, 2, 3, 4, 5, 7, 8, 10] # expected result [["abcd","bc"]] # next input argument list [1] # next expected result, and so on ] # the next test group and test [ string_multiply ["a", 0] "" ["a", 3] "aaa" ] ] ~~~ the name of the test and the calling "this" context can also be provided by using an array instead of a function.. ~~~ ["name", function] [this_context, function] ~~~ test execution with reporting ~~~ test_runner = new test_runner_class test_runner.execute tests ~~~ the customizable default reporting format ~~~ string-indices 1 2 3 4 5 6 7 8 9 string-multiply 1 2 3 string-replace-string 1 2 3 4 5 6 7 8 9 string-quote 1 failure string-quote 2 inp "t'est" exp "\"t'est\"x" out "\"t'est\"" ~~~ * inp: the arguments to the test-procedure * exp: the expected result * out: the actual result instead of writing to standard output, it can also create an array of test results for each group, which could be processed by other tools. ~~~ results = test_runner.execute_tests tests ~~~ # the implementation ~~~ class test_runner_class is_string: (a) -> typeof a is "string" to_json: (a) -> JSON.stringify(a).replace /,(?=\S)/g, ", " is_plain_object: (x) -> x? and typeof x is "object" and x.constructor is Object object_merge: (a, b) -> for k, v of b if @is_plain_object(v) and @is_plain_object(a[k]) a[k] = @object_merge a[k], v else a[k] = v a report_compact_failure_strings: (inp, exp, out) -> [((@to_json a for a in inp).join ", "), @to_json(exp), @to_json(out)] report_compact: (results) -> for [name, test_results...] in results process.stdout.write name for [status, index, name, inp, exp, out] in test_results if status then process.stdout.write " #{index}" else [inp_string, exp_string, out_string] = @report_compact_failure_strings inp, exp, out process.stdout.write [ "\n failure #{name} #{index}" "inp #{inp_string}" "exp #{exp_string}" "out #{out_string}" ].join "\n " console.log "" constructor: (options) -> default_options = reporter: @report_compact @options = @object_merge default_options, options @options.reporter = @options.reporter.bind @ execute_tests: (tests) -> status = true for [f, rest...] in tests break unless status [name, context] = if Array.isArray f [name_or_context, f] = f if @is_string(name_or_context) then [name_or_context, null] else [f.name, name_or_context] else [f.name, null] results = [name] for i in [0...rest.length] by 2 inp = rest[i] exp = rest[i + 1] out = f.apply context, inp out_string = @to_json out exp_string = @to_json exp status = out_string == exp_string results.push [status, i / 2, name, inp, exp, out] break unless status results execute: (tests) -> @options.reporter @execute_tests tests ~~~