使用 Jasmine 在 CoffeeScript 中测试 Backbone.js 时出现类型错误
我目前正在浏览 Backbone.js 上的 PeepCode 视频,但尝试将其全部编写在 CoffeeScript 中而不是纯粹的 JavaScript。
到目前为止一切顺利,除了当我尝试对代码运行 Jasmine 测试时遇到一些 TypeErrors:
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
Jasmine 测试规范如下:
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: Cannot call method 'isFirstTrack' of undefined
if I run album.isFirstTrack(0)
in the console on the page it returns < code>true 因此窗口确实可以访问变量和方法。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
原因是范围界定,是的。
当您声明不带
var
关键字的album
变量且未将其附加到另一个对象时,您将在全局范围内创建一个变量。这是一个坏主意。你确实想限制你创建的全局对象的数量,因为另一块 JavaScript 很容易覆盖你的数据,或者导致冲突等。
你在测试中调用
this.model
的想法从这个角度来说是正确的。问题是您需要将model
附加到this
才能做到这一点。很简单:您所要做的就是说
this.album = ...
就完成了。现在您的测试将能够找到this.album
并且一切都应该正常。the reason is scoping, yes.
when you declare the
album
variable with novar
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 attachmodel
tothis
to be able to do that. easy enough:all you have to do is say
this.album = ...
and you're done. now your tests will be able to findthis.album
and everything should work.(注意:我相信 Derick 的答案是正确的 - 它解释了
TypeError: Cannot call method 'isFirstTrack' of undefined
消息 - 但这个答案也可能是相关的。)你说
这意味着如果您删除
jQuery ->
行,那么您的测试就会通过?如果是这样的话,那就不是范围问题了;这是一个代码顺序问题。当您将函数传递给 jQuery 时,该函数在文档准备好之前不会执行。同时,您的测试将在定义Album
和AlbumView
类之前立即运行。没有理由使用
jQuery ->
包装器,因为您的类定义不依赖于任何现有的 DOM 元素。(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
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 theAlbum
andAlbumView
classes are defined.There's no reason to use the
jQuery ->
wrapper, since your class definitions don't depend on any existing DOM elements.