2012-02-28 39 views
6

我很难理解如何设置一个对象,允许我测试我的jQuery调用。我不需要嘲笑任何异步调用或任何东西,只是基本的使用。因此,让我阐述了自己的功能,我想测试一下(截断为简单起见):嘲笑jQuery来测试基本的使用

listGamesCallback : function(data) { 
    var gameList = $("#gameList select"); 

    gameList.empty(); 

    $.each(data, function() { 
      var newOption = $('<option>', {value : this.gameId }); 
      newOption.text(string); 
      newOption.data("isJoinable", isJoinable); 

      // Add it to the list 
      gameList.append(newOption); 
    }); 


} 

我需要在这里嘲笑了jQuery单元测试这种方法,但我无法弄清楚如何做到这一点在JavaScript中。即使没有jsMockito,我也不知道如何使用jQuery在这种情况下的属性创建一个对象。任何帮助,这将不胜感激。

我正在使用jsTestDriver,jsHamcrest,jsMockito和jQuery。然而,创建具有这些属性的$对象的一般化方法也是非常棒的。谢谢!

对于那些问,这是我想出了,似乎有点工作..但我不明白为什么。

var saved$ = $; 

var mockContruct = mockFunction(); 
var mockedGamelist = mock(jQuery); 
var mockedOption = mock(jQuery); 

mocked$ = (function() { 
    var test = function(name) { 
     var args = jQuery.makeArray(arguments); 
     return mockContruct.call(test, args); 
    }; 

    $.extend(test, $); 

    // This is what confuses me. This worked, but it's wierd 
    // It allows me to use the regular jQuery functions like 
    // $.each, while returning mocked objects when selectors are used. 
    test.prototype.constructor = test; 

    return test; 
})(); 

$ = mocked$;  

when(mockContruct).call(anything(), hasItem(containsString("#gameList"))) 
    .thenReturn(mockedGamelist); 

when(mockContruct).call(anything(), hasItems(containsString("<option>"), both(object()).and(hasMember("value")))) 
     .thenReturn(mockedOption); 

headerFunctions.listGamesCallback([ { 
    gameId : 1, 
    isWhitesTurn : false, 
    isGameOver : false, 
    whiteUserName : "foobar", 
    blackUserName : "barfoo" 
} ]); 

JsMockito.verify(mockedGamelist).empty(); 
JsMockito.verify(mockedGamelist).append(mockedOption); 

$ = saved$; 
+0

如果你提供了你的测试代码,这也会很有用。 :) – alpian 2012-02-29 12:50:37

+0

好的,对于迟到的回复抱歉,但我已被重新分配,所以这得到了较低的优先级。我有一种错综复杂的方式来运行我编入问题的测试。它写得不好,因为我不太了解jQuery如何做它的功能。 – CrazyBS 2012-03-10 04:03:55

+0

您编写的测试并不能真正证明您的代码执行以外的其他任何内容。如果失败,它也会污染其他有问题的测试,因为如果失败则不会恢复$。我认为你仍然错过了这些测试的重点:你应该为你的代码期望的**行为编写一个测试,而不是它如何执行的细节。您应该尝试编写一个测试,该测试仅着重于在您的函数运行后证明gameList具有X选项以及其中的任何值。 – alpian 2012-03-11 22:40:49

回答

1

好的,这里我想到了用最少的设置完成这项工作。 .extend在这里完全有必要,以便jQuery对象正确设置。这允许你模拟构造函数以返回可用于运行测试的模拟jQuery对象。作为一名间谍,jQuery在所有情况下都能按预期工作,除非你希望它做其他事情。那就是:

TestCase("HeaderTest", { 
    testListGamesCallback : function() { 
     var saved$ = $; 

     $ = $.prototype.construct = jQuery.extend(spy(jQuery), jQuery); 

     var mockGameList = mock(jQuery); 
     when($)(containsString("#gameList")).thenReturn(mockGameList); 

     headerFunctions.listGamesCallback([ { 
      gameId : 1, 
      isWhitesTurn : false, 
      isGameOver : false, 
      whiteUserName : "foobar", 
      blackUserName : "barfoo" 
     } ]); 

     verify(mockGameList).empty(); 
     verify(mockGameList).append(object()); 

     $ = saved$; 
    } 
}); 

需要说明的该解决方案是,嘲讽比构造以外的任何是有点棘手。你将不得不设置你想要模拟的每个单独的函数,然后编程行为。所以:

$.each = mockFunction(); 
when($.each)(...matchers...).thenReturn(...); 

但它仍然允许测试你需要什么。

0

不知道如果我明白你的意思,但如果你想为你们例如创建“数据”,这是我知道的方法:

var data = [ { id : 1 , name : 'foo' } , { id : 2, name : 'bar' ] ​ 

但 - 如果你想创建一个选项列表,比你的代码需要一对夫妇的修复: 看到http://jsfiddle.net/7MMap/

var data = [ { gameId : 1 , name : 'foo' ,isJoinable:true} , { gameId : 2, name : 'bar' ,isJoinable:false}] 

    listGamesCallback = function(data) { 
    var gameList = $("#gameList select") 
        .empty(); 

    $.each(data, function(i,d) { 
      var newOption = $('<option>', {value : d.gameId }) 
      .text(d.name) 
      .data("isJoinable", d.isJoinable); 

      // Add it to the list 
      gameList.append(newOption); 
    }) 
      }; 

listGamesCallback(data); 
+0

实际上,jQuery.each()函数允许您直接使用'this'来访问该值。所有这些.text()。data()使嘲笑和恕我直言变得复杂,这使得它更不可读。但这只是意见。感谢您格式良好的答案。不过我仍然在嘲笑jQuery。 – CrazyBS 2012-03-10 04:17:51

0

惩戒jQuery是不是嘲讽是。你应该只是嘲笑你的对象的协作者。 jQuery为您提供了一些实用程序 - 它不是合作者,因此不应该被嘲笑。

你在这里合作的是DOM,或者你的代码和DOM之间的一些中间对象。按照Avi的建议,data是一个值对象,可以在您的测试中创建。

在我的JS测试中,我没有模拟DOM,我使用真正的DOM,并且一定会拆除我在测试之间创建的任何东西,这似乎工作得很好。

+0

我同意我在这种情况下过度测试。然而,我的想法是,因为我希望我的测试尽可能孤立,我不希望jQuery实际做任何事情,我只需要验证它是否被使用。但是,这确实需要使用jQuery来通过测试。所以你有一个观点。 – CrazyBS 2012-03-10 04:15:03

+0

我可以嘲笑DOM吗? – CrazyBS 2012-03-10 04:27:35

+0

当然,你**可以**,但在你的情况下,我实际上只是在你的测试中,添加一个REAL选择与ID gameList到REAL DOM,然后运行你的函数,然后在它运行证明(说使用CSS选择器)DOM看起来应该是这样 - 然后通过删除选择来自行清理。忘了试图嘲笑jQuery - 这是一个实用程序 - 你不应该这样做:)。 – alpian 2012-03-11 22:43:28

1

作为alpian回答的扩展,您可以创建DOM元素而无需将它们添加到页面中。让你的JS功能采取的相关要素作为参数:

listGamesCallback : function(data, gameListSelectElem) { 
    var gameList = $(gameListSelectElem); 
    ... 

,并对其进行测试,像这样:

var fakeSelect = $('<select>'), 
    data = ...; 

listGamesCallback(data, fakeSelect[0]); 

equal(fakeSelect.find('option').length, 1, 'must have exactly 1 option'); 
... 

的最后一行代码上面是qUnit。采取任何你需要的,重点是说你可以传递一个从未添加到页面的DOM元素,然后使用jQuery调查该DOM元素以查找它是否被正确操作。