Matthew
Matthew

Reputation: 15662

Mocha, should.js and asserting an exception

I have a file app.coffee:

class TaskList

class Task
    constructor: (@name) ->
        @status = 'incomplete'
    complete: ->
        if @parent? and @parent.status isnt 'completed'
          throw "Dependent task '#{@parent.name}' is not completed."
        @status = 'complete'
        true
    dependsOn: (@parent) ->
        @parent.child = @
        @status = 'dependent'

# Prepare scope stuff
root = exports ? window
root.TaskList = TaskList
root.Task = Task

and a file called test/taskTest.coffee:

{TaskList, Task} = require '../app'
should = require 'should'

describe 'Task Instance', ->
    task1 = task2 = null
    it 'should have a name', ->
        something = 'asdf'
        something.should.equal 'asdf'
        task1 = new Task 'feed the cat'
        task1.name.should.equal 'feed the cat'
    it 'should be initially incomplete', ->
        task1.status.should.equal 'incomplete'
    it 'should be able to be completed', ->
        task1.complete().should.be.true
        task1.status.should.equal 'complete'
    it 'should be able to be dependent on another task', ->
        task1 = new Task 'wash dishes'
        task2 = new Task 'dry dishes'
        task2.dependsOn task1
        task2.status.should.equal 'dependent'
        task2.parent.should.equal task1
        task1.child.should.equal task2
    it 'should refuse completion it is dependent on an uncompleted task', ->
        (-> task2.complete()).should.throw "Dependent task 'wash dishes' is not completed."

If I run this command in terminal: mocha -r should --compilers coffee:coffee-script -R spec I have a failing test (the final one) saying that it was expecting an exception "Dependent task 'wash dishes' is not completed." but got 'undefined'.

If I change (-> task2.complete()).should.throw to -> task2.complete().should.throw by removing the parenthesis, the test passes, and fails if I don't throw the exception. But if I change the exception message to something random, it still passes. Am I doing something wrong? Shouldn't the test only pass if the message is literally "Dependent task 'wash dishes' is not completed."?

Upvotes: 8

Views: 5855

Answers (2)

Jacob
Jacob

Reputation: 3639

First off, that's some good looking Coffeescript.

Second, David Weldon is correct in his answer that you can just change the throw to actually throw an error and it works.

Here's your code put into one long file with just the throw changed.

class TaskList

class Task
    constructor: (@name) ->
        @status = 'incomplete'
    complete: ->
        if @parent? and @parent.status isnt 'completed'
          throw new Error "Dependent task '#{@parent.name}' is not completed."
        @status = 'complete'
        true
    dependsOn: (@parent) ->
        @parent.child = @
        @status = 'dependent'

# Prepare scope stuff
root = exports ? window
root.TaskList = TaskList
root.Task = Task

should = require 'should'

describe 'Task Instance', ->
    task1 = task2 = null
    it 'should have a name', ->
        something = 'asdf'
        something.should.equal 'asdf'
        task1 = new Task 'feed the cat'
        task1.name.should.equal 'feed the cat'
    it 'should be initially incomplete', ->
        task1.status.should.equal 'incomplete'
    it 'should be able to be completed', ->
        task1.complete().should.be.true
        task1.status.should.equal 'complete'
    it 'should be able to be dependent on another task', ->
        task1 = new Task 'wash dishes'
        task2 = new Task 'dry dishes'
        task2.dependsOn task1
        task2.status.should.equal 'dependent'
        task2.parent.should.equal task1
        task1.child.should.equal task2
    it 'should refuse completion it is dependent on an uncompleted task', ->
        (-> task2.complete()).should.throw "Dependent task 'wash dishes' is not completed."

Mocha that bastard and you're good to go.

Upvotes: 3

David Weldon
David Weldon

Reputation: 64342

You are throwing an exception with a string instead of throwing an error object. throw() looks for the latter. So your original code works if you do:

throw new Error "Dependent task '#{@parent.name}' is not completed."

If something you write in CoffeeScript is producing results that make no sense, try compiling it to js (or pasting the code into try CoffeeScript. You'll see that:

-> task2.complete().should.throw "Dependent task 'wash dishes' is not completed."

compiles to:

(function() {
  return task2.complete().should["throw"]("Dependent task 'wash dishes' is not completed.");
});

which just defines a function and does not execute it. This explains why changing the string makes no difference. I hope that helps.

Upvotes: 4

Related Questions