Having problems spying method calls on a Backbone Router to ensure it calles the right method on a given route.
excerpt from the test
describe 'Router', ->
beforeEach ->
@router = new App.Router()
Backbone.history.start()
afterEach ->
Backbone.history.stop()
describe 'routes', ->
it 'should be defined', ->
expect(@router.routes).toBeDefined()
describe 'default route', ->
it 'should be defined', ->
expect(@router.routes['']).toBeDefined()
it 'should call index', ->
spy = spyOn(@router, "index")
@router.navigate('', true)
expect(spy).toHaveBeenCalled()
The router
class App.Router extends Backbone.Router
routes:
'' : 'index'
index: ->
console.log "router.index has been called"
Everything passes except the last test "should call index". It fails with the message "Expected spy index to have been called". Ive tried other variants
it "should call index", ->
spyOn(@router, "index")
@router.navigate('', true)
expect(@router.index).toHaveBeenCalled()
I can also see the "router.index has been called" log output in the test output from the original Router.index function
Thanks!
EDIT: One solution
describe '#1 Solution', ->
it 'should call index', ->
spyOn(App.Router.prototype, "index")
@router = new App.Router()
Backbone.history.start()
@router.navigate('', true)
expect(App.Router.prototype.index).toHaveBeenCalled()
It has took too much time to me to come with a working jsFiddle and the question has been already answered by @MarkRushakoff.
Still I have some comments.
The way Backbone is binding the routes make very difficult to test it.
The point is that the router methods are not called directly in the router instance, the methods are taked as callbacks and stored in an internal Backbone.history.route
waiting for execution, check the Backbone.Router.route code.
This operation is done in the moment the Router
is instantiate, so you have to spy
your Router.method before you instantiate the reference, so for you have to delay Backbone.history.start
also after the spy
has been activated.
As you have to declare the spy
before the router instance is created you have to do it in a Class level.
Said so this is the simplest solution I came with:
describe("Router", function() {
afterEach( function(){
Backbone.history.stop();
});
it("should call index", function(){
spyOn(App.Router.prototype, "index")
var router = new App.Router(); // instance created after spy activation
Backbone.history.start(); // it has to start after the Router instance is created
router.navigate('', true);
expect(App.Router.prototype.index).toHaveBeenCalled();
});
});
Conclusion, I think the Backbone.Router
implementation has not an intuitive design.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With