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

Posted

技术标签:

【中文标题】使用 Jasmine 监视 Backbone.js 路由调用【英文标题】:Spying on Backbone.js route calls with Jasmine 【发布时间】:2012-08-04 13:10:37 【问题描述】:

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

测试摘录

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"

除了最后一个测试“应该调用索引”之外,一切都通过了。 它失败并显示消息“预期的间谍索引已被调用”。 我试过其他变种

it "should call index", ->
    spyOn(@router, "index")
    @router.navigate('', true)
    expect(@router.index).toHaveBeenCalled()

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

谢谢!

编辑: 一种解决方案

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

【问题讨论】:

【参考方案1】:

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

我还有一些cmets。

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

重点是路由器方法不是直接在路由器instance中调用的,方法是作为callbacks存放在一个内部Backbone.history.route等待执行,check the Backbone.Router.route code。

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

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

这么说,这是我提供的最简单的解决方案:

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 的实现没有直观的设计。

【讨论】:

谢谢!看起来这是要走的路:) 这不是测试路由触发的可行方法。所有这些测试都是索引函数作为回调绑定到路由器,而不是在触发路由时实际调用该函数。请参阅this S.O. post,了解如何验证路由器匹配 url 时是否调用了相应的路由函数。【参考方案2】:

我很确定这与 Backbone 在您使用路由散列时绑定到其路由方法的方式有关(尤其是当您看到控制台日志正确输出时)。也就是说,路由器绑定了原来的index方法,但是你的spy已经替换了“当前”的index方法。

你有两个选择:

spyOn(@router, "index") 在路由器绑定到路由之前(可能很难) 窥探原型的index方法:spyOn(App.router.prototype, "index"); @router.navigate('', true); expect(App.router.prototype.index).toHaveBeenCalled();

【讨论】:

谢谢,这可行:) 选项二是可行的。在创建路由器的新实例之前,需要添加间谍。我想没关系! 选项一我认为是不可能的,因为 routes binding 是在 initialize 中完成的,所以在您引用实例的那一刻绑定已经完成。

以上是关于使用 Jasmine 监视 Backbone.js 路由调用的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jasmine 监视值属性(而不是方法)

使用 Jasmine 监视私有变量的属性/函数

如何在 Jasmine 1 中监视 Falcor 数据模型构造函数

Jasmine - 监视在同一文件中调用的函数

可以将 webpack 4 模块配置为允许 Jasmine 监视其成员吗?

Jasmine:监视一个名为 X 次的函数,并获得第 n 次调用