Converting from Jasmine to QUnit

I had to convert a sizeable test suite from Jasmine to QUnit. The former has a wide array of matchers and situation specific helpers for explicitly testing things, and the latter is about as barebones as it gets. This is all well and good, it just means converting is a pain.

First, here are some regexes to help. These are vim substitution commands, but you should be able to adapt them for any editor with a regex find & replace. Also, these were written for Coffeescript code which had some optional brackets here and there, so YMMV, but hopefully they are useful.

%s/\vdescribe "(.+)", \-\>/module "\1"/
%s/\vit "/test "/
%s/\vexpect\((.+)\)\.toEqual\(?(.+)\)?/equals \1, \2/
%s/\vexpect\((.+)\)\.toBeTruthy\(\)/ok \1/
%s/\vjasmine\.createSpy/createSpy/
%s/\vexpect\((.+)\).toHaveBeenCalledWith\((.+)\)/deepEqual \1.lastCallArguments, [\2]/
%s\vbeforeEach \-\>/setup: ->/

We change describe calls to module, it "..." to test "...", some basic expectations from Jasmine to the equivalents in QUnit, and then the beforeEach calls from Jasmine to setup options for the module call. This got me most of the way there, after running them I had to fix the indentation, ensure the setup: stuff was passed as an option to module (it was a function call before), and fix some brackets here and there. If you come up with more or better regexes please leave a comment and I’ll add them to the post!

Next, I ripped out a very simple version of the jasmine.Spy object which I started to really miss in QUnit. This is my super basic copy of the Jasmine implementation, suitable for both command line execution through node-qunit or the in browser test runner.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
exports = if window? then window else global

class Spy
  constructor: (original) ->
    @called = false
    @callCount = 0
    @calls = []
    @original = original
    @fixedReturn = false

  whichReturns: (value) ->
    @fixedReturn = true
    @fixedReturnValue = value
    @

createSpy = (original) ->
  spy = new Spy

  f = (args...) ->
    f.called = true
    f.callCount++
    f.lastCall =
      object: this
      arguments: args

    f.lastCallArguments = f.lastCall.arguments
    f.calls.push f.lastCall

    unless f.fixedReturn
      f.original?.call(this, args...)
    else
      f.fixedReturnValue

  for k, v of spy
    f[k] = v

  f

spyOn = (obj, method) ->
  obj[method] = createSpy(obj[method])

exports.createSpy = createSpy
exports.spyOn = spyOn

Hopefully this will be of use to you if you end up doing anything similar! I have this here mostly for future reference but I hope it helps at least one of you tube surfers out there.