Qual è il problema?
Una delle cose abbastanza incasinate da testare sono gli elementi dell’elementi AJAX, come l’AutoComplete. Non sono mai riuscito a testare l’evento che corrisponde direttamente allo use case, ovvero, l’utente seleziona un’elemento della lista e nel campo associato viene inserito un valore, perlomeno con YUI. Ho provato anche con Selenium ma senza successo. Forse impegnandosi di più si potrebbe ottenere qualcosa di meglio ma, visti i risultati (vedi discussione in fondo), non so se sia necessario.
Riduciamo i danni
In mancanza dell’ideale possiamo comunque ridurre di molto la porzione di codice non testata. In primo luogo possiamo testare in modo estensivo la data source associata all’AutoComplete. In secondo luogo possiamo usare i custom events per isolare e testare il codice che si occupa delle conseguenze della selezione.
Lo scenario da testare
Facciamo un esempio classico: un AutoComplete che, alla selezione del nome del cliente, imposta anche il valore della primary key di quel record in un campo nascosto.
Usiamo i custom events
Se non tenessimo conto delle esigenze dei test, la soluzione sarebbe semplice:
clienteAC.itemSelectEvent.subscribe(
function(sType, args) {
clienteAC.getInputEl().value = args[2].nome;
document.form-cliente.id_cliente.value = args[2].id;
}
);
Invece, con gli eventi custom possiamo scrivere questo:
clienteAC.itemSelectEvent.subscribe(
function(sType, args) {
customItemSelectEvent.fire({
selectedItem: args[2]
});
}
);
Ovvero, alla selezione dell’utente, lanciamo un evento che contiene la selezione stessa.
Dall’altra parte, il listener raccoglie l’evento, esegue le operazioni necessarie e al termine lancia un secondo evento, comunicando la fine dell’operazione:
// aggiorniamo il campo associato all’auto complete
clienteAC.getInputEl().value = args[2].nome;
// impostamo il valore della primary key
document.form-cliente.id_cliente.value = args[2].id;
// “avvisiamo” che abbiamo finito
customLoadCompleteEvent.fire("Finished");
Testiamo!
Questo secondo evento è essenziale per i test, infatti, a questo punto possiamo semplicemente inviare un customItemSelectEvent e aspettare l’altro evento in risposta:
//…
name: "Test auto complete cliente",
testSelectEvent: function() {
var resume = function (ev, type) {
this.resume(function() {
customLoadComplete.unsubscribe(resume);
YAHOO.util.Assert.areEqual(
"Mario Rossi",
document.form-cliente.nome.value,
"Il nome del cliente dovrebbe essere 'Mario Rossi'"
);
});
};
customLoadComplete.subscribe(resume, this, true);
customItemSelect.fire({
selectedItem:{
id: 1,
nome: "Mario Rossi"
}
});
this.wait();
},
Un bilancio
Un dubbio legittimo che può venire è se valga la pena di complicare così il codice solo per poterlo testare. È un dubbio che ho spesso!
In generale, l’effetto delle modifiche in funzione della “testabilità” è positivo. L’importante è non spingersi al punto di creare metodi o variabili che vengono usate solo nei test: a mio parere è un errore.
In questo caso credo che il codice risultante sia più pulito e meglio separato. Adesso possiamo sfruttare l’evento finale customLoadComplete in coda alla selezione dell’utente per aggiornare altri elementi della pagina, senza che ci sia una dipendenza diretta con l’AutoComplete.
All’inizio accennavo al fatto che, nonostante tutto, sia forse possibile testare direttamente l’AutoComplete. Tuttavia ho l’impressione che avrei ottenuto una qualità inferiore, con più rigidità. A questo punto lo prenderei in considerazione solo come test complementare, utile ma non indispensabile.