【问题标题】:Jasmine testing coffeescript expect(setTimeout).toHaveBeenCalledWithJasmine 测试coffeescript expect(setTimeout).toHaveBeenCalledWith
【发布时间】:2012-11-15 12:37:52
【问题描述】:

在学习Jasmine的过程中,遇到了这个问题。我想要一个基本功能运行,然后设置超时再次调用自己......简单的东西。

class @LoopObj
  constructor: ->
  loop: (interval) ->
    #do some stuff
    setTimeout((=>@loop(interval)), interval)

但我想测试以确保使用正确的参数调用 setTimeout

describe "loop", ->
  xit "does nifty things", ->
  it "loops at a given interval", ->
    my_nifty_loop = new LoopObj
    interval = 10
    spyOn(window, "setTimeout")
    my_nifty_loop.loop(interval)
    expect(setTimeout).toHaveBeenCalledWith((-> my_nifty_loop.loop(interval)), interval)

我收到此错误:Expected spy setTimeout to have been called with [ Function, 10 ] but was called with [ [ Function, 10 ] ]

这是因为(-> my_nifty_loop.loop(interval)) 函数不等于(=>@loop(interval)) 函数吗?或者它与第二个[ [ Function, 10 ] ] 周围的额外方括号有关?还有别的吗?

我哪里出错了?

【问题讨论】:

    标签: tdd coffeescript jasmine


    【解决方案1】:

    我对 CoffeeScript 不太了解,但是你可以通过替换来调试

    expect(setTimeout).toHaveBeenCalledWith((-> my_nifty_loop.loop(interval)), interval)
    

    expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), interval)
    

    并重新运行规范。我认为如果多余的方括号消失,那么你的问题是因为你有两个不同的函数引用。如果它们没有消失,那么您的 LoopObj 定义就会有些奇怪,可能是胖箭头运算符(在我的 n00b 眼中看起来没有必要)。

    【讨论】:

    • jasmine.any(Function) 有效。我很确定要使@等于正确的this,必须使用粗箭头。现在的问题变成了“这是一个令人满意的测试吗?”。我们已经证明已经调用了一些 setTimeout,但是测试并不能确保循环再次触发。我应该用waits() 来监视它吗?还是别的什么方法?还是没关系?
    • 罗杰说:胖箭。我认为这是一个有效的测试概念;只需正确引用 LoopObj.loop 即可让您的设计完美运行​​。规范期望本身是否将 my_nifty_loop.loop(interval) 包装在匿名函数中?同样,通过 n00b 的眼睛, (-> my_nifty_loop ...) 看起来像函数定义。
    【解决方案2】:

    使用 bind 辅助函数。

    比较 setTimeout 的参数时遇到问题的原因是它隐藏在 lambda 中。您创建的每个 lambda 表达式都是不同的。在这种情况下,lambda 实际上除了将函数绑定到 this 的正确值之外没有添加任何值。

    事实证明,这是 JavaScript 中的一种常见模式,Underscore.js 库中有一个名为 bind 的函数正是这样做的。

    通过分解此模式并将其与 setTimeout(与 _.delay 相同)组合,我们可以比较传递给它的参数,因为它们不会被包装在 lambda 中。

    window.bindAndDelay = (wait, fn, obj, args...) ->
      setTimeout((-> obj.fn(args...)), wait)
    
    class @LoopObj
      constructor: ->
      loop: (interval) ->
        #do some stuff
        bindAndDelay(interval, @loop, @, interval)
    

    然后在你的测试中:

    describe "loop", ->
      xit "does nifty things", ->
      it "loops at a given interval", ->
        my_nifty_loop = new LoopObj
        interval = 10
        spyOn(window, "bindAndDelay")
        my_nifty_loop.loop(interval)
        expect(bindAndDelay).toHaveBeenCalledWith(interval, my_nifty_loop.loop, my_nifty_loop, interval)
    

    我选择将延迟量作为bindAndDelay 的第一个参数,以便它适用于应用了任意数量参数的函数。

    另一种方法是使用_.bindAll,但你必须记住在每个对象上都使用它,而且它不利于带参数的函数。所以我认为上面的更好。

    这应该可行,但我仍然认为它很臭。所以如果其他人有更好的答案,请发布!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多