使用jasmine测试对象中的事件处理程序

时间:2012-08-10 15:09:47

标签: javascript event-handling coffeescript jasmine

我在CoffeeScript ala Spinejs中使用了一个简单的MVC模式。它允许我通过在实例化之前指定事件对象来添加事件侦听器:

class Controller extends Module
    @include Events

    eventSplitter: /^(\S+)\s*(.*)$/
    events: {}

    constructor: (view = '<div/>') ->
        @view = $(view)
        @delegate key, func for key, func of @events

    delegate: (key, func) ->
        match = key.match @eventSplitter
        if match[2] is ''
            @view.on( match[1], $.proxy(@[func], @) )
        else
            @view.on( match[1], match[2], $.proxy(@[func], @) )

快速解释 - 传入一个选择器,元素或html字符串,该字符串将分配给view属性。然后,委托方法接受事件对象并循环遍历并将事件侦听器分配给视图节点本身,或者将子事件处理程序从子元素委托给视图节点:

class Con extends Controller
    events:
        'click' : 'parent'
        'mouseover span' : 'child'

    parent: (e) ->
        console.log('div was clicked')

    child: (e) ->
        console.log('span was moused over')

con = new Con('<div><span>test</span></div>')

con.view.trigger('click') // --> 'div was clicked'
con.view.find('span') // --> 'span was moused over'

我正在努力用Jasmine来测试这个。以下测试将通过:

expect( con.view ).toHandle('mouseover')
expect( con.view ).toHandle('click')

至少允许我测试某种处理程序设置,但我想测试正在设置的实际处理程序。我正在试图窥探事件处理程序,但它们似乎永远不会被调用。

spyOn(con, 'parent')
con.view.trigger('click')

expect( con.parent ).toHaveBeenCalled() // fails!

我也试图窥探原型中的实际方法。

spyOn( Con.prototype, 'parent' )
con.view.trigger('click')

expect( Con.prototype.parent ).toHaveBeenCalled() // fails!

如果我明确地调用该方法,我可以通过测试:

con.parent();
expect( con.parent ).toHaveBeenCalled() // passes!

我认为spy在事件执行时失败,因为事件处理程序是通过jQuery'on'方法引用的。因为它是一个函数,但它不是con对象上的实际方法。

任何帮助将不胜感激。任何一般的测试建议也是受欢迎的。

2 个答案:

答案 0 :(得分:1)

解决方案似乎是在对象之外定义间谍。我刚刚发现了jasmine.createSpy()方法,它创建了一个裸体间谍,正是我所需要的:

describe "Controller", ->
    clickSpy = jasmine.createSpy()
    mouseSpy = jasmine.createSpy()

    class Con extends CRDGN.Controller
        events:
            'click' : 'parent'
            'mouseover span' : 'child'

        parent: clickSpy

        child: mouseSpy

    con = new Con('<div><span>test</span></div>')

it "should attach listeners through an events object", ->
    con.view.trigger('click')
    expect( clickSpy ).toHaveBeenCalled()

    con.view.find('span').trigger('mouseover')
    expect( mouseSpy ).toHaveBeenCalled()

答案 1 :(得分:1)

Jasmine实际上有spyOn method,允许您在不覆盖System Under Test的情况下执行此操作。

我承认没有运行此特定代码,但这是您的答案的重构,使用spyOn并拆分您的两个测试。

describe 'CRDGN.Controller', ->
  it 'should attach a click listener', =>
    clickSpy = spyOn(CRDGN.Controller, 'parent')
    controller = @_buildController()

    controller.view.trigger('click')

    expect(clickSpy).toHaveBeenCalled()

  it 'should attack a mouseover listener for <span>', =>
    mouseSpy = spyOn(CRDGN.Controller, 'child')
    controller = @_buildController()

    controller.view.find('span').trigger('mouseover')

    expect(mouseSpy).toHaveBeenCalled()

  _buildController: () ->
    new CRDGN.Controller('<div><span>test</span></div>')