0

I have this piece of CoffeeScript code in one of my Backbone views:

myMethod: ->
     # some code here

     $.when(
          # ...
     ).done(
          @myCallback
     )

and I wanna test that myCallback is called in the done block.

I'm not sure how to do this in Mocha.js + Sinon.js. I was able to spy on jquery and check that the when method is called:

spy = sinon.spy($, 'when')
@view.myMethod()
spy.called.should.be.true
spy.restore()

But I can't do the same with the done block because, if I have unterstood it right, it's related to the deferred object returned by the when method.

I also tried something like this:

# NOT WORKING CODE
stub = sinon.stub($.Deferred(), 'done').returns
     myCallback: sinon.stub()
@view.myMethod()
stub.called.should.be.true
stub.restore()

but still getting:

expected false to be true

Any ideas? :)

lucke84
  • 4,516
  • 3
  • 37
  • 58
  • shouldn't you be using `@myCallback` instead of `@myCallback()`? Just a blind guess. But $.done doesn't exists. `done` is part of the promise object, so I guess it would be `$.Deferred().done` – pocesar Jan 23 '13 at 17:30
  • @pocesar you're right, it's a typo (once I haven't used copy and paste.. sorry!). I agree with you about the deferred, I wrote it like that to make clear what I would like to achieve. I tried to spy on that and I also tried to stub the promise object, without success. Further ideas? – lucke84 Jan 23 '13 at 17:37

2 Answers2

1

You can stub $.when similarly as you would do an ajax request. Firstly you have to stub $.when and return a $.Deferred object.

var stub = sinon.stub($, "when", function (event) {
    var result = $.Deferred();
    result.args = event;
    return result;
})

After calling the function you want to test, get the return value of the specific $.when call which will be the deferred object. Resolve it manually and you can do the assertion of the @myCallback being called.

@view.myMethod()

// Get first when call
var call = $.when.getCall(0).returnValue;

// To return successful when call (having used .done() )
call.resolve();
0

Mock it in the view class?

#arrange
view = new View
view.myCallback = sinon.spy()

#act
view.myMethod()

#assert
view.myCallback.called.should.be.true

Whether the callback was called as a deferred done handler or any other way is an implementation detail. All that should matter that the method was called (with correct arguments, if you choose).

jevakallio
  • 35,324
  • 3
  • 105
  • 112
  • it still seems that the function hasn't been called. Maybe the done callback could not be triggered for some reasons? – lucke84 Jan 23 '13 at 17:56
  • Maybe your test found a bug :) Btw. I'm not sure my sinon assert is correct, just copied from your question. Only ever used sinon with Jasmine asserts. – jevakallio Jan 23 '13 at 17:59
  • If so, I hope to find it out :) The function is working, it has been written without tests before I could notice it and I don't want to leave it uncovered. The assert is fine, used many times before, in fact I get a "good error". – lucke84 Jan 23 '13 at 18:02
  • Is the deferred an async operation? Do you need to declare tests as async with Mocha? – jevakallio Jan 23 '13 at 18:13
  • Or maybe if the deferred is async, the callback is not YET called at the time of assert. Don't know how to handle that with Mocha, though. – jevakallio Jan 23 '13 at 18:14
  • Maybe you're right and it's not yet called when I assert my expectation. How would you deal with that (even with jasmine)? – lucke84 Jan 24 '13 at 09:59
  • In Jasmine you would use `waitsFor`. See https://github.com/pivotal/jasmine/wiki/Asynchronous-specs – jevakallio Jan 24 '13 at 11:42