Mondi su mondi, sistemi di sistemi.

Test della GUI: un esempio con l’autocomplete di YUI

Qual è il problema?

Una delle cose abba­stanza inca­si­nate da testare sono gli ele­menti dell’elementi AJAX, come l’AutoComplete. Non sono mai riu­scito a testare l’evento che cor­ri­sponde diret­ta­mente allo use case, ovvero, l’utente sele­ziona un’elemento della lista e nel campo asso­ciato viene inse­rito un valore, per­lo­meno con YUI. Ho pro­vato anche con Selenium ma senza suc­cesso. Forse impe­gnan­dosi di più si potrebbe otte­nere qual­cosa di meglio ma, visti i risul­tati (vedi discus­sione in fondo), non so se sia necessario.

Riduciamo i danni

In man­canza dell’ideale pos­siamo comun­que ridurre di molto la por­zione di codice non testata. In primo luogo pos­siamo testare in modo esten­sivo la data source asso­ciata all’AutoComplete. In secondo luogo pos­siamo usare i custom events per iso­lare e testare il codice che si occupa delle con­se­guenze della selezione.

Lo sce­na­rio da testare

Facciamo un esem­pio clas­sico: un AutoComplete che, alla sele­zione del nome del cliente, impo­sta anche il valore della pri­mary key di quel record in un campo nascosto.

Usiamo i custom events

Se non tenes­simo conto delle esi­genze dei test, la solu­zione sarebbe sem­plice:

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 pos­siamo scri­vere que­sto:

clienteAC.itemSelectEvent.subscribe(
    function(sType, args) {
        customItemSelectEvent.fire({
            selectedItem: args[2]
        });
    }
);

Ovvero, alla sele­zione dell’utente, lan­ciamo un evento che con­tiene la sele­zione stessa.

Dall’altra parte, il liste­ner rac­co­glie l’evento, ese­gue le ope­ra­zioni neces­sa­rie e al ter­mine lan­cia un secondo evento, comu­ni­cando 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 è essen­ziale per i test, infatti, a que­sto punto pos­siamo sem­pli­ce­mente inviare un customItemSelectEvent e aspet­tare l’altro evento in rispo­sta:

//…
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 bilan­cio

Un dub­bio legit­timo che può venire è se valga la pena di com­pli­care così il codice solo per poterlo testare. È un dub­bio che ho spesso!

In gene­rale, l’effetto delle modi­fi­che in fun­zione della “testa­bi­lità” è posi­tivo. L’importante è non spin­gersi al punto di creare metodi o varia­bili che ven­gono usate solo nei test: a mio parere è un errore.

In que­sto caso credo che il codice risul­tante sia più pulito e meglio sepa­rato. Adesso pos­siamo sfrut­tare l’evento finale customLoadComplete in coda alla sele­zione dell’utente per aggior­nare altri ele­menti della pagina, senza che ci sia una dipen­denza diretta con l’AutoComplete.

All’inizio accen­navo al fatto che, nono­stante tutto, sia forse pos­si­bile testare diret­ta­mente l’AutoComplete. Tuttavia ho l’impressione che avrei otte­nuto una qua­lità infe­riore, con più rigi­dità. A que­sto punto lo pren­de­rei in con­si­de­ra­zione solo come test com­ple­men­tare, utile ma non indispensabile.

Per proseguire

Commenti e trackback sono disabilitati.