2011-12-15 11 views
22

Sto provando a migliorare con il mio codice JavaScript. Ho il seguente codice:Interrompere una chiamata al selettore jQuery?

var categoryVal = $('#category').val(); 
if (categoryVal === '') { 
    doSomething(); 
} 

La mia prova corridore non si ha il #category di ingresso sulla pagina, così come avrei stub/finto fuori il selettore jQuery qui? Ho esaminato sia la documentazione jasmin che la documentazione sinon, ma non riesco a capire come farli funzionare qui, dal momento che i loro stub operano sugli oggetti, che non è lo $.

+2

Non ho usato nessuna di queste librerie, ma '$' è un oggetto infatti (le funzioni sono oggetti in JavaScript). A parte questo, le funzioni di jQuery funzionano quando non è selezionato nulla - '.val()' restituirà 'undefined'. – pimvdb

+0

Ho capito che '.val()' restituirà 'undefined', ma come posso quindi stubare per testare il mio' === '' '? – swilliams

risposta

33

Il problema qui è che $() è una funzione che restituisce un oggetto con il metodo val(). Quindi devi stub $() per restituire un oggetto stoppato con il metodo val.

$ = sinon.stub(); 
$.withArgs('#category').returns(sinon.stub({val: function(){}})); 

Ma l'errore principale è quello di lasciare che il codice che si desidera testare chiamare la funzione $() per creare nuove istanze. Perché? È consigliabile non creare nuove istanze nella classe, ma passarle nel costruttore. Diciamo che avere la funzione che otterrà un valore di un ingresso, doppio, e scrivere di nuovo ad un altro:

function doubleIt(){ 
    $('#el2').val(('#el1').val() *2); 
} 

In questo caso è creare 2 nuovi oggetti chiamando $(). A questo punto è necessario eseguire lo stub $() per restituire un mock e uno stub. Utilizzando l'esempio seguente è possibile evitare questo:

function doubleIt(el1, el2){ 
    el2.val(el1.val() *2); 
} 

Mentre, nel primo caso si deve stub $ per restituire uno stub, nel secondo caso si può facilmente passare una stub e una spia nella vostra funzione.

Quindi il test Sinon per la seconda sarebbe simile a questa:

var el1 = sinon.stub({val: function(){}}); 
    el1.returns(2); 

var el2 = sinon.spy({val: function(){}}, 'val') 

doubleIt(el1, el2) 

assert(el2.withArgs(4).calledOnce) 

Così, mentre non si hanno elementi DOM qui, si può semplicemente testare la logica dell'applicazione senza bisogno di creare lo stesso dom come nella tua app.

+2

Ho finito per fare qualcosa del genere. Ho refactored tutte le mie manipolazioni DOM in funzioni separate e lo stubing delle chiamate a quelle funzioni. Ha il vantaggio di mantenere i miei metodi più svelti. – swilliams

+1

Grazie per la spiegazione dettagliata del problema. Sto riscontrando questo problema nel nodo js, ​​ma ottengo un errore quando tento di assegnare a $ dicendo che $ non è definito. Immagino che sia dalla modalità rigorosa. Qualche idea su come aggirare questo? –

+0

Non dovrebbe esserci un globale di fronte a $ in caso di IIFE? – Jackie

1

Ecco una guida piuttosto buona per testare le visualizzazioni se si utilizza Backbone.js e Jasmin. Scorri verso il basso fino alla sezione Visualizza.

http://tinnedfruit.com/2011/04/26/testing-backbone-apps-with-jasmine-sinon-3.html

È vero, gli stub operano su oggetti. Immagino il punto di creare uno stub di visualizzazione in questo modo.

this.todoViewStub = sinon.stub(window, "TodoView") 
     .returns(this.todoView); 

È solo per poter eseguire successivamente il rendering della vista.

this.view.render(); 

In altre parole, aggiungere il '#category' div al DOM del testrunner, in modo che $ può agire su di esso. Se il tuo div "#category" non è in this.view, probabilmente puoi semplicemente creare una pagina test.html in cui esegui il test isolato. Questo è un modello comune nel framework MVC Javascript che sono più abituato a quel Backbone.

Ecco un semplice esempio struttura dell'applicazione JMVC:

/todo 
    /models 
     todo.js 
    /list 
     /views 
     init.tmpl 
     listItem.tmpl 
     list.css   
     list.js  (Controller) 
     unitTest.js (Tests for your list.) 
     list_test.html (A html for your unit tests to run on.) 

Avere questa impostazione si può solo includere il div "#category" nel list_test.html se non si dispone già capita di avere dentro una dei punti di vista

+0

Bello, grazie per il collegamento ;-) –

9

jQuery utilizza il motore del selettore css Sizzle sotto il cofano ed è stato disaccoppiato in modo che ci siano solo pochi punti in cui si aggancia. È possibile intercettarlo per evitare qualsiasi interazione con il dom.

jQuery.find è l'importante da modificare per rispondere con qualsiasi cosa tu voglia. Sinon potrebbe essere usato qui o temporaneamente scambiando la funzione.

esempio

existingEngine = jQuery.find 
jQuery.find = function(selector){ console.log(selector) } 
$(".test") 
//>> ".test" 
jQuery.find = existingEngine 

si potrebbe applicare anche una condizione cattura specifico con un ripiego

existingEngine = jQuery.find 
jQuery.find = function(selector){ 
    if(selector=='blah'}{ return "test"; } 
    return existingEngine.find.apply(existingEngine, arguments) 
} 

Nel mio recente lavoro ho fatto un oggetto fittizio che risponde come un nodo DOM e avvolto che nel un oggetto jQuery. Questo risponderà quindi a val() correttamente e avrà tutti i metodi jquery presenti che si aspetta. Nel mio caso sto semplicemente tirando i valori da un modulo. Se stai facendo una vera manipolazione potresti dover essere più intelligente di questo, magari creando un dom dom temporaneo con jQuery che rappresenta ciò che ti aspettavi.

obj = { 
    value: "blah", 
    type: "text", 
    nodeName: "input", 
} 
$(obj).val(); // "blah" 
+0

Questa è la migliore risposta, grazie! @tissak –

+0

Questo è troppo strettamente collegato agli interni di jQuery. Il test dell'unità si interromperà se jQuery cambia il modo in cui usa sfrigolare o cessa di usarlo. Il test unitario suppone solo di interrompere se c'è un bug nel codice sotto test. – Phil