Using Jasmine to test events


#1

I would like to test what happens in when an event occurs. I’ve just started learning Jasmine and I’m not quite sure how to do this part. Basically, when an event goes off, it should call a function. I want to expect that the function has been called correctly. I’ve been trying to figure this out for quite a while now, so if anyone could help, that’d be great. Here’s what I have right now.

foo.coffee:

module.exports = 
  function: ->
    $(window).on 'resize', => @functionToCall()

  functionToCall: ->
    console.log "I'm being called!"

foo-spec.coffee:

Foo = require 'foo'
describe 'Testing events', ->
  it 'should call Foo.FunctionToCall after the window has been resized', ->
    spyOn($.fn, 'on').andCallThrough()
    spyOn(Foo, 'FunctionToCall')
    # I think I have to do something here to mock the event?
    waitsFor ->
      Foo.function()
    runs ->
      expect($.fn.on).toHaveBeenCalledWith('resize', Jasmine.any(Function)) #Also can't get jasmine.any(Function) to work
      expect(Foo.functionToCall).toHaveBeenCalled()

I’m pretty sure the issue is that Foo.function returns an event instead of a Boolean or a Promise (for waitsForPromise). Anyway, thanks for the help.


#2

I think that @mark_hahn is probably better at the event stuff than me, but …

spyOn replaces the named function on the given object with a mock that offers certain capabilities. So, if I understand your code correctly, your test is:

  1. Creating a mock object and replacing the on method of $.fn
  2. Calling Foo.function() which (in theory … I’m not up on jQuery’s $.fn stuff yet) triggers the on mock which calls through to the actual jQuery on method and associates an anonymous function with the resize event
  3. Expects Foo.functionToCall to have been called … which fails

Because:

  1. To my understanding, toHaveBeenCalled only works on Spies. Except Foo.functionToCall isn’t a Spy, $.fn.on() is.
  2. Even if you made Foo.functionToCall into a Spy, Foo.functionToCall hasn’t been called because the resize event hasn’t been triggered from what I can see in your test.

So maybe the solution is:

  1. Don’t make $.fn.on a Spy because you don’t use it
  2. Make Foo.functionToCall into a Spy because it seems that is really what you want to test
  3. Figure out a way to trigger the resize event so you can test things

Does that help?


#3

Whoops, I forgot to add some stuff to the code. I do have Foo.functionToCall as a spy, and I was checking to be sure that $(window).on was called with the correct arguments. That’s probably not 100% necessary in this situation, but for other more complicated situations, like dealing with keyboard events, it would be nice to be able to verify that it was called.

I’ll edit my top post to include the lines I forgot.


#4

Adding the lines that you did doesn’t change the fact that you still don’t trigger the resize event that I can see. The code it seems you’re trying to test is:

$(window).on 'resize', => @functionToCall()

You want to make sure that when the resize event is triggered, it actually calls @functionToCall(). But you’re not triggering the resize event, so @functionToCall() doesn’t get called.

On the flip side, it seems you understand how the jQuery on method works. So why are you testing it?


#5

I’m not triggering the event because I’m not sure how. I should have been clearer on that.

I’d be okay with not testing that the jQuery function was called, but it doesn’t hurt and it should still work with andCallThrough. My actual event handler is more complicated than this. This is really just an example that will give me enough information in order to test my real handler.


#6

And I figured it out. This will create an event, init it to be a ready event, then tell the window to dispatch that event. It must be after the testing function is called and due to asyncness, it must be within the waitsFor function.

The new waitsFor function:

    waitsFor ->
      ret = Foo.function()
      evt = document.createEvent 'Event'
      evt.initEvent 'ready', false, false
      window.dispatchEvent evt
      ret

Thanks for the help.