Javascript 使用Jasmine在CoffeeScript中测试Backbone.js时出现类型错误

Javascript 使用Jasmine在CoffeeScript中测试Backbone.js时出现类型错误,javascript,backbone.js,coffeescript,jasmine,Javascript,Backbone.js,Coffeescript,Jasmine,我目前正在处理这个问题,但试图用CoffeeScript而不是纯JavaScript来编写它 到目前为止还不错,但当我尝试对代码运行Jasmine测试时,遇到了一些类型错误: TypeError: Cannot call method 'isFirstTrack' of undefined TypeError: Cannot call method 'get' of undefined 我的CoffeeScript/Backbone文件如下所示: jQuery -> class wi

我目前正在处理这个问题,但试图用CoffeeScript而不是纯JavaScript来编写它

到目前为止还不错,但当我尝试对代码运行Jasmine测试时,遇到了一些类型错误:

TypeError: Cannot call method 'isFirstTrack' of undefined
TypeError: Cannot call method 'get' of undefined
我的CoffeeScript/Backbone文件如下所示:

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

});
茉莉花测试规范如下:

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

});
我猜问题与CoffeeScript将所有内容包装在函数中有关。当我从等式中删除jQuery时,它工作得很好。
奇怪的是,即使Jasmine告诉我
TypeError:如果我在控制台的页面上运行
album.isFirstTrack(0)
,它返回
true
,那么窗口确实可以访问变量和方法。

原因是作用域,是的

在声明不带
var
关键字的
album
变量且不将其附加到另一个对象时,您正在全局范围内创建一个变量

这是个坏主意。您确实希望限制创建的全局对象的数量,因为另一块javascript很容易覆盖您的数据,或导致冲突等

从这个角度来看,您在测试中调用
this.model
的想法是正确的。问题是,您需要将
模型
附加到
才能执行此操作。很简单:

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

你所要做的就是说
this.album=…
,你就完成了。现在,您的测试将能够找到此相册,并且一切都应该正常。

原因在于范围,是的

在声明不带
var
关键字的
album
变量且不将其附加到另一个对象时,您正在全局范围内创建一个变量

这是个坏主意。您确实希望限制创建的全局对象的数量,因为另一块javascript很容易覆盖您的数据,或导致冲突等

从这个角度来看,您在测试中调用
this.model
的想法是正确的。问题是,您需要将
模型
附加到
才能执行此操作。很简单:

  beforeEach(function () {
    this.album = new Album(albumData[0]);
  });
你所要做的就是说
this.album=…
,你就完成了。现在,您的测试将能够找到此相册,并且一切都应该正常。

注意:我相信Derick的答案是正确的,它解释了
类型错误:无法调用未定义的
消息的方法'isFirstTrack',但此答案也可能是相关的。)

你说

当我从等式中删除jQuery时,它工作得很好

也就是说,如果你删掉了
jQuery->
,那么你的测试通过了吗?如果是这样,那就不是范围问题;这是一个代码顺序问题。当您将函数传递给jQuery时,在文档准备就绪之前不会执行该函数。同时,您的测试在定义
相册
相册视图
类之前立即运行

没有理由使用
jQuery->
包装器,因为您的类定义不依赖于任何现有的DOM元素。

注意:我相信Derick的回答是正确的,它解释了
类型错误:无法调用未定义的
消息的方法'isFirstTrack',但这个答案也可能是相关的。)

你说

当我从等式中删除jQuery时,它工作得很好

也就是说,如果你删掉了
jQuery->
,那么你的测试通过了吗?如果是这样,那就不是范围问题;这是一个代码顺序问题。当您将函数传递给jQuery时,在文档准备就绪之前不会执行该函数。同时,您的测试在定义
相册
相册视图
类之前立即运行


没有理由使用
jQuery->
包装器,因为您的类定义不依赖于任何现有的DOM元素。

在深入研究了更多内容之后,我已经设法修复了它。从Jasmine测试规范中的两个
expect
语句中删除
this
关键字似乎是可行的。我仍然有兴趣理解为什么——这是一个范围问题吗?问题不是CoffeeScript的函数包装器。通过将这两个类定义为
window.Album
window.AlbumView
,您已经正确地导出了这两个类。在深入研究之后,我成功地修复了它。从Jasmine测试规范中的两个
expect
语句中删除
this
关键字似乎是可行的。我仍然有兴趣理解为什么——这是一个范围问题吗?问题不是CoffeeScript的函数包装器。通过将这两个类定义为
window.Album
window.AlbumView
,您已经正确地导出了这两个类。对,
Album=
this.Album=
是不等价的(除非
this
window
,这不是Jasmine在每次调用
回调之前调用
的情况)。对,
album=
this.album=
是不等价的(除非
this
window
,Jasmine在每次
回调之前调用
时就不是这样了)。回答很好,建议也很好。除非jQuery准备就绪时需要运行一些东西,否则jQuery->根本不需要。这是一个很好的答案和一个很好的建议。除非jQuery准备就绪时需要运行某些东西,否则jQuery->根本就不是必需的。