Last active
October 1, 2015 09:58
-
-
Save shesek/1971059 to your computer and use it in GitHub Desktop.
Lightweight CoffeeScript unit testing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Just a fun experiment I hacked together :-) | |
## Basic handling | |
results = current = {} | |
group = (format) -> (name, func) -> | |
previous = current | |
(current.sub ||= []).push current = name: format.replace '%s', name | |
try | |
previous.beforeEach?() | |
do func | |
current.passed = true unless current.pending or current.failed? | |
catch e then current.failed = e | |
current = previous | |
beforeEach = (func) -> current.beforeEach = func | |
## Async handling | |
async = async_notify = null | |
do -> | |
awaiting = 0 | |
callbacks = [] | |
async = (func) -> -> | |
res = current | |
res.pending = true | |
++awaiting | |
done = -> res.pending = false; do fn for fn in callbacks if --awaiting is 0 | |
func.call | |
fail: fail = (e) -> res.failed = e; do done | |
passed: passed = -> res.passed = true; do done | |
mark: (func) -> -> | |
[previous, current] = [current, res] | |
try func.apply this, arguments; current=previous; do passed | |
catch e then current=previous; fail e | |
async_notify = (func) -> | |
return do func if awaiting is 0 | |
callbacks.push func | |
## Assertions | |
eq = (a, b) -> throw new Error "#{a} not equal to #{b}" unless a is b | |
neq = (a, b) -> throw new Error "#{a} equal to #{b}" unless a isnt b | |
lt = (a, b) -> throw new Error "#{a} not larget than #{b}" unless a > b | |
gt = (a, b) -> throw new Error "#{a} not smaller than #{b}" unless a < b | |
ok = (a) -> throw new Error "Should be truthy: #{a}" unless a | |
nok = (a) -> throw new Error "Should be falsy: #{a}" unless not a | |
## Some test groups for cool DSL. Quite easy to customize. | |
test = group '%s' | |
module = group 'Module: %s' | |
klass = group 'Class: %s' | |
func = group 'Function: %s' | |
method = group 'Method: %s' | |
describe = group '%s' | |
it = group 'it %s' | |
## Some functions for displaying results | |
countFailed = (res) -> | |
res._fails ||= do -> | |
count = +res.failed? | |
count += countFailed s for s in res.sub if res.sub | |
count | |
display = (res, level = -1) -> | |
if level > -1 | |
console.log ((new Array level+1).join ' ') + res.name + ' -- ' + | |
if res.pending then 'pending' | |
else if res.passed | |
if (count = countFailed res) > 0 then 'failed tests: ' + count else 'passed' | |
else if res.failed then 'failed: ' + res.failed | |
display sub, level+1 for sub in res.sub if res.sub | |
## Actual tests looks like this | |
module 'JavaScript natives', -> | |
klass 'String', -> | |
it 'Allows you to compare strings', -> | |
eq 'foo', 'foo' | |
neq 'foo', 'bar' | |
it 'Has some cool methods', -> | |
method 'substr()', -> | |
it 'lets you get a part of a string', -> | |
eq ('abcd'.substr 1,2), 'bcX' | |
method 'charAt()', -> | |
it 'lets get a single char', -> | |
eq ('abc'.charAt 2), 'c' | |
describe 'ES5 methods', -> | |
method 'trim()', -> it 'trims spaces from a string', -> | |
eq (' abc '.trim()), 'abc' | |
klass 'Array', -> | |
it 'has some cool methods', -> | |
method 'concat', -> it 'allows you to concat arrays', -> | |
eq ([1,2,3].concat [4,5,6])[4], 5 | |
test 'Asynchronous tests', -> | |
# Asynchronous tests are decorated with the `async` function and use `@fail` and `@passed` | |
it 'fails', async -> | |
setTimeout (=>@fail 'FOO'), 2000 | |
it 'passes', async -> | |
setTimeout @passed, 1500 | |
# Alternatively, the `@mark` decorator can be used to mark the test group for async callbacks. | |
# This works for exceptions and for nesting tests. If a function that was `@mark`d doesn't throw, | |
# its considered to be succesfull. | |
describe '@mark', -> | |
it 'can decorate async functions and correctly identify test group', async -> | |
setTimeout (@mark -> throw new Error "FAIL."), 1500 | |
describe 'an example with fs.stat', async -> | |
(require 'fs').stat '/should/exists/', @mark (err, stats) -> | |
it 'should be a directory', -> | |
nok err | |
ok stats.isDirectory() | |
describe 'beforeEach', -> | |
it 'runs before every test in the same level as the beforeEach() call', -> | |
val = null | |
beforeEach -> val = 123 | |
it 'should be 123', -> eq val, 123; val = 456 | |
it 'should stay 123', -> | |
eq val, 123 | |
val = 456 | |
it 'should not run on tests in different nesting level', -> | |
eq val, 456 | |
# Display when everything is ready | |
async_notify -> display results | |
### | |
$ coffee cunit.coffee | |
Module: JavaScript natives -- failed tests: 1 | |
Class: String -- failed tests: 1 | |
it Allows you to compare strings -- passed | |
it Has some cool methods -- failed tests: 1 | |
Method: substr() -- failed tests: 1 | |
it lets you get a part of a string -- failed: Error: bc not equal to bcX | |
Method: charAt() -- passed | |
it lets get a single char -- passed | |
ES5 methods -- passed | |
Method: trim() -- passed | |
it trims spaces from a string -- passed | |
Class: Array -- passed | |
it has some cool methods -- passed | |
Method: concat -- passed | |
it allows you to concat arrays -- passed | |
Asynchronous tests -- failed tests: 3 | |
it fails -- failed: FOO | |
it passes -- passed | |
@mark -- failed tests: 2 | |
it can decorate async functions and correctly identify test group -- failed: Error: FAIL. | |
an example with fs.stat -- failed tests: 1 | |
it should be a directory -- failed: Error: Should be falsy: Error: ENOENT, no such file or directory '/should/exists/' | |
beforeEach -- passed | |
it runs before every test in the same level as the beforeEach() call -- passed | |
it should be 123 -- passed | |
it should stay 123 -- passed | |
it should not run on tests in different nesting level -- passed | |
### |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment