2012-02-28 18 views
6

Bardzo trudno mi zrozumieć, jak skonfigurować obiekt, który pozwala mi przetestować moje wywołania jQuery. Nie muszę wyśmiewać żadnych połączeń asynchronicznych ani niczego, tylko podstawowe użycie. Więc pozwól mi ustawić mój funkcji, które chcę przetestować (obcięty dla uproszczenia):Kpiny z jQuery do testowania podstawowego użycia

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


} 

muszę kpić jQuery tutaj do testów jednostkowych tej metody, ale jestem w stanie dowiedzieć się, jak to zrobić w javascript. Nawet bez jsMockito nie wiem, jak utworzyć obiekt z właściwościami, które jQuery ma w tej sytuacji. Przydałaby się jakakolwiek pomoc.

Używam jsTestDriver, jsHamcrest, jsMockito i jQuery. Jednak ogólne podejście do tworzenia obiektu $, który ma te właściwości, byłoby również niesamowite. Dziękuję Ci!

Dla tych, którzy o to pytali, oto, co wymyśliłem, co wydawało się być dobrą pracą ... ale nie rozumiem dlaczego.

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

Przydałoby się, jeśli podałeś również swój kod testowy. :) – alpian

+0

Ok, przepraszam za spóźnioną odpowiedź, ale zostałem ponownie przypisany, więc otrzymałem niższy priorytet. Mam skomplikowany sposób na przeprowadzenie testu, który zredagowałem na pytanie. Nie jest napisany bardzo dobrze, ponieważ nie bardzo rozumiem, w jaki sposób jQuery robi to, co robi. – CrazyBS

+0

Test, który napisałeś, tak naprawdę nie dowodzi niczego innego niż twój kod. Może także zanieczyścić inne testy problemami, jeśli się nie powiedzie, ponieważ nie przywróci $, jeśli się nie powiedzie. Myślę, że nadal brakuje ci punktów tych testów: powinieneś napisać test na ** zachowanie **, którego oczekujesz od swojego kodu, a nie szczegóły tego, jak to działa. Powinieneś spróbować napisać test, który po prostu skupi się na udowodnieniu, że gameList ma opcje X z dowolnymi wartościami w nich po uruchomieniu funkcji. – alpian

Odpowiedz

1

OK, tutaj to, co wymyśliłem, wykonuje pracę przy minimalnym ustawieniu. Konieczne jest tutaj .extend, aby obiekt jQuery był poprawnie skonfigurowany. Pozwala to kpić z konstruktora, aby zwrócił wyśmiewane obiekty jQuery, których można użyć do uruchomienia testów. Jako szpieg, jQuery będzie działał zgodnie z oczekiwaniami we wszystkich sytuacjach, z wyjątkiem sytuacji, gdy chcesz zrobić coś innego. Oto ona:

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

Krytycznym dla tego rozwiązania jest to, że kpiny z czegokolwiek innego niż konstruktor są nieco trudne. Będziesz musiał ustawić każdą indywidualną funkcję, którą chcesz udawać, a następnie zaprogramować zachowanie. A więc:

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

Ale nadal pozwala na testowanie tego, czego potrzebujesz.

0

Nie jestem pewien, czy rozumiem, co masz na myśli, ale jeśli chcesz, aby utworzyć „dane” dla ciebie przykład, jest to metoda wiem:

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

ale - jeśli chcesz utworzyć lista opcji, niż ty kodu potrzebuje kilku poprawek: zobaczyć 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

W rzeczywistości funkcja jQuery.each() umożliwia bezpośredni dostęp do wartości za pomocą "this". Wszystkie te .text(). Data() komplikuje kpiny i IMHO sprawia, że ​​jest mniej czytelny. Ale to tylko opinia. Dziękuję za dobrze sformatowaną odpowiedź. Nadal jednak próbuję wyśmiać jQuery. – CrazyBS

0

Mocking jQuery nie jest to, co jest za szyderczy. Powinieneś tylko kpić z obiektu współpracowników. jQuery dostarcza ci niektórych narzędzi - nie jest to współpracownik, a więc nie powinno się kpić.

To, z czym współpracujesz, to DOM lub jakiś obiekt pośredni między Twoim kodem a DOM. data to obiekt wartości i można go po prostu utworzyć w teście zgodnie z sugestią Avi.

W moich testach JS, nie kpię z DOM, używam prawdziwego DOM i na pewno zburzę wszystko, co stworzyłem pomiędzy testami i wydaje mi się, że działa całkiem nieźle.

+0

Zgadzam się na moje nadmierne testowanie w tej sytuacji. Jednak moim zdaniem, ponieważ chcę, aby mój test był jak najbardziej odizolowany, nie chcę, aby jQuery rzeczywiście coś zrobił, wystarczy sprawdzić, czy został użyty. Wymaga to jednak, aby jQuery zawsze był używany do zdania testu. Masz więc rację. – CrazyBS

+0

Czy mogę kpić z DOM ?! – CrazyBS

+0

Cóż, oczywiście, że ** możesz **, ale w twoim przypadku chciałbym tylko, w twoim teście, dodać REAL select z id gameList do REAL DOM, a następnie uruchomić swoją funkcję, a następnie po jej uruchomieniu udowodnić (powiedzieć z selektorami CSS), że DOM wygląda tak, jak powinien - następnie posprzątaj po sobie, usuwając zaznaczenie. Zapomnij o próbach wyśmiewania jQuery - to narzędzie - i nie powinieneś tego robić :). – alpian

1

Jako rozszerzenie odpowiedzi Alpiana można tworzyć elementy DOM bez konieczności dodawania ich do strony.Dokonać funkcje JS podjęcia odpowiednich elementów jako parametry:

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

i przetestować je tak:

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

listGamesCallback(data, fakeSelect[0]); 

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

Ostatni wiersz kodu powyżej jest dla qUnit. Weź wszystko, czego potrzebujesz, chodzi o to, że możesz przekazać element DOM, który nigdy nie został dodany do strony, a następnie zbadaj ten element DOM za pomocą jQuery, aby sprawdzić, czy został poprawnie zmanipulowany.

Powiązane problemy