hellosmithy
hellosmithy

Reputation: 279

TypeError testing Backbone.js in CoffeeScript with Jasmine

I'm currently working through the PeepCode video on Backbone.js but attempting to write it all in CoffeeScript rather than bare JavaScript.

So far so good, except when I try to run Jasmine tests on the code I run into some TypeErrors:

TypeError: Cannot call method 'isFirstTrack' of undefined
TypeError: Cannot call method 'get' of undefined

My CoffeeScript/Backbone file looks like this:

jQuery ->
  class window.Album extends Backbone.Model
    isFirstTrack: (index) ->
    index is 0

  class window.AlbumView extends Backbone.View
    tagName:    'li'
    className:  'album'

    initialize: ->
      @model.bind('change', @render)
      @template = _.template $('#album-template').html()     
    render: =>
      renderedContent = @template @model.toJSON()
      $(@el).html(renderedContent)
      return this

And the Jasmine test spec like this:

var albumData = [{
  "title":  "Album A",
  "artist": "Artist A",
  "tracks": [
    {
        "title": "Track A",
        "url": "/music/Album A Track A.mp3"
    },
    {
        "title": "Track B",
        "url": "/music/Album A Track B.mp3"
    }]
}, {
  "title": "Album B",
  "artist": "Artist B",
  "tracks": [
    {
        "title": "Track A",
        "url": "/music/Album B Track A.mp3"
    },
    {
        "title": "Track B",
        "url": "/music/Album B Track B.mp3"
  }]
}];

describe("Album", function () {

  beforeEach(function () {
    album = new Album(albumData[0]);
  });

  it("creates from data", function () {
    expect(this.album.get('tracks').length).toEqual(2);
  });

  describe("first track", function() {
    it("identifies correct first track", function() {
      expect(this.album.isFirstTrack(0)).toBeTruthy();
    })
  });

});

I'm guessing the problem has something to do with CoffeeScript wrapping everything in a function. When I remove jQuery from the equation it works fine. Strangely though even though Jasmine is telling me TypeError: Cannot call method 'isFirstTrack' of undefined if I run album.isFirstTrack(0) in the console on the page it returns true so the window does have access to the variables and methods.

Upvotes: 0

Views: 1588

Answers (2)

Trevor Burnham
Trevor Burnham

Reputation: 77416

(Note: I believe Derick's answer is correct—it explains the TypeError: Cannot call method 'isFirstTrack' of undefined message—but this answer may also be pertinent.)

You say

When I remove jQuery from the equation it works fine.

Meaning that if you cut out the line jQuery ->, then your tests pass? If that's the case, it's not a scope issue; it's a code order issue. When you pass a function to jQuery, that function isn't executed until the document is ready. Meanwhile, your tests are running immediately—before the Album and AlbumView classes are defined.

There's no reason to use the jQuery -> wrapper, since your class definitions don't depend on any existing DOM elements.

Upvotes: 2

Derick Bailey
Derick Bailey

Reputation: 72868

the reason is scoping, yes.

when you declare the album variable with no var keyword and without attaching it to another object, you are creating a variable in the global scope.

this is a bad idea. you really want to limit the number of global objects that you create, because it's easy for another chunk of javascript to overwrite your data, or cause conflicts, etc.

your idea of calling this.model in the tests is correct from this perspective. the problem is that you need to attach model to this to be able to do that. easy enough:

  beforeEach(function () {
    this.album = new Album(albumData[0]);
  });

all you have to do is say this.album = ... and you're done. now your tests will be able to find this.album and everything should work.

Upvotes: 2

Related Questions