Skip to content

Instantly share code, notes, and snippets.

@shesek
Last active October 1, 2015 09:58
Show Gist options
  • Save shesek/1971059 to your computer and use it in GitHub Desktop.
Save shesek/1971059 to your computer and use it in GitHub Desktop.
Lightweight CoffeeScript unit testing
# 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