2012-02-28 16 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元素以查找它是否被正確操作。