使用 Jasmine 监视 Backbone.js 路由调用

在监视骨干路由器上的方法调用以确保它在给定路由上调用正确的方法时遇到问题.

Having problems spying method calls on a Backbone Router to ensure it calles the right method on a given route.

测试摘录

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()

路由器

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()

我还可以在原始Router.index函数的测试输出中看到router.index has been called"日志输出

I can also see the "router.index has been called" log output in the test output from the original Router.index function

谢谢!

一种解决方案

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()

推荐答案

我花了太多时间来提供 working jsFiddle 并且@MarkRushakoff 已经回答了这个问题.

It has took too much time to me to come with a working jsFiddle and the question has been already answered by @MarkRushakoff.

我还有一些意见.

Backbone 绑定路由的方式使得测试变得非常困难.

The way Backbone is binding the routes make very difficult to test it.

重点是路由器methods并不是直接在路由器instance中调用的,方法是作为callbacks存放在一个内部 Backbone.history.route 等待执行,检查 Backbone.Router.route 代码.

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.

这个操作是在 Router 被实例化的那一刻完成的,所以你必须在实例化引用之前 spy 你的 Router.method,所以你必须在 spy 被激活之后延迟 Backbone.history.start.

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.

由于您必须在创建路由器实例之前声明 spy,因此您必须在 Class 级别进行.

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();  
  });
});

结论,我认为Backbone.Router的实现没有直观的设计.

Conclusion, I think the Backbone.Router implementation has not an intuitive design.

相关文章