Skip to Content

Unit Testing Meteor With Tiny Test

One way to structure large Meteor apps is to split your app into many small packages, and tie them together using dependency injection. Meteor comes with the awesometinytest package allowing you to unit test your packages with the meteor test-packages command. It also comes with a nice test viewer:

Tinytest has a super-simple API. You can either run a synchronous test with Tinytest.add:

Tinytest.add 'one plus one equals two', (test) ->
  test.equal 1 + 1, 2

Or you can run an asynchronous tests with Tinytest.addAsync:

Tinytest.addAsync 'callback does not return an error', (test, next) ->
  callback (err) ->
    test.isNull err
    next()

That’s it! When you create a package with meteor create –package username:packagename Tinytest will be setup by default to test your new package (more details on writing packages here)

Tinytest can also nest and group tests. To do this simply use a dash ’ - ‘ in the test descriptions to split groups of similar tests ie:

Tinytest.add 'maths - one plus one equals two', (test) ->
  test.equal 1 + 1, 2

Tinytest.add 'maths - one minus one equals zero', (test) ->
  test.equal 1 - 1, 0

Tinytest.add 'english - zebra has five letters', (test) ->
  test.equal 'zebra'.length, 5

Notice how the tests are now grouped into ‘maths’ and ’english’:

By splitting the descriptions with dashes, you can have as many subgroups as you like.

Extending Tinytest

Although Tinytest is great, spending my dayjob writing tests with Rspec and Ginkgo leaves me yearning for describe, context, and it blocks during my evening Meteor adventures.

Thankfully it’s only a few lines of code to add this functionality to Tinytest:

describe = (->
  _describe = (list, desc, fn) ->
    list.push desc
    this.describe = this.context = _describe.bind(this, list)
    this.it = (d, f) ->
      if f.length <= 1
        Tinytest.add list.concat(d).join(' - ') , f
      else
        Tinytest.addAsync list.concat(d).join(' - '), f
    fn.bind(this)()
    list.pop()
)().bind(this, [])

By adding this code you can now use describe, context, and it blocks as you would in a regular testing framework (make sure you start with a describe block):

describe 'division', ->
  context 'when the divisor is not zero', ->
    it 'returns a number', (test) ->
      test.equal 12 / 4, 3

  context 'when the divisor is zero', ->
    it 'returns infinity', (test) ->
      test.equal 12 / 0, Infinity

describe 'an asynchronous test', ->
  it 'also just works', (test, next) ->
    sendThree = (callback) -> callback(3)
    sendThree (n) ->
      test.equal n, 3
      next()

Note that asynchronous tests also work just by adding a next callback argument.

For more examples of Tinytest in action, a large portion of the base Meteor packages are themselves tested with Tinytest (https://github.com/meteor/meteor/tree/devel/packages).

Edit: The above code is now its own package on atmosphere. Use it instead of Tinytest in your package.js files like so:

Package.onTest(function(api) {
  api.use('peterellisjones:describe');
  api.addFiles('thing_to_test.js', 'server');
});