Mondi su mondi, sistemi di sistemi.

Utility del giorno: Fake

Monday, July 12th, 2010

Una delle cose più noiose è il dover ripetere manualmente una serie di operazioni con il browser: per verificare che tutto sia a posto; come test per la gui; nell’accesso a servizi che richiedono la ripetizione di una serie sempre uguali di passi.

Ci sono già strumenti di automazione e replay come Selenium, ma sono pensati esclusivamente per il testing.

Fake è un’applicazione basata su Safari che copre sia gli scenari più standard di Selenium sia i casi in cui vogliamo automatizzare l’accesso notoriamente impervio a siti come quelli di home banking e simili. In più, Fake permette alcune operazioni molto più potenti e che non hanno nulla a che vedere con il testing ma che pos­sono essere utilis­sime come l’esecuzione di script di shell o AppleScript.

L’interfaccia è un incrocio fra Automator e Safari ed è di una facilità estrema: basta trascinare le azioni nella timeline e impostare i para­metri. Una volta messa a punto la sequenza che vogliamo, pos­siamo salvarla per il futuro.

Progettare al buio?

Friday, October 30th, 2009

Dato che lavoro praticamente solo in ambito web mi capita a volte di pensare con un po’ d’invidia a chi sviluppa applicazioni desktop, dove ha a disposizione strumenti come l’Interface Builder.

“Usa i dati, Luke…”

Stamattina, però, sentendo “The MDN show” mi è venuto in mente un post di Luke Wroblesky letto poco tempo prima.

In quel post viene fatto notare come il design su web sia sempre più legati a fattori quantitativi (sperimentali, quasi) che derivano dall’analisi dei dati degli utenti.

According to Gemmell

Viceversa, nel podcast, Matt Gemmell dà una serie di consigli – utilis­simi, peraltro – sull’usabilità delle applicazioni desktop e mi sono reso conto che non esistono strumenti analitici come sul web; non che io sappia, perlomeno. È vero che la situazione è diversa, a partire dagli ovvi problemi di privacy, ma credo la cosa sarebbe fattibile.

Ad esempio, sarebbe utilis­simo sapere quali sono le funzioni più usate; come vengono impostate le pre­ferenze e così via. Possibile che non ci abbia mai pensato nessuno?

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

Wednesday, July 29th, 2009

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 discus­sione in fondo), non so se sia necessario.

Riduciamo i danni

In mancanza dell’ideale pos­siamo comunque ridurre di molto la porzione di codice non testata. In primo luogo pos­siamo testare in modo estensivo la data source associata all’AutoComplete. In secondo luogo pos­siamo usare i custom events per isolare e testare il codice che si occupa delle conseguenze della selezione.

Lo scenario da testare

Facciamo un esempio clas­sico: 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 tenes­simo 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 pos­siamo 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 neces­sarie 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 pos­siamo 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 pos­siamo 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 pos­sibile 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.

pgTAP: un esempio concreto di TDD

Wednesday, May 27th, 2009

Qualche giorno fa parlavo di pgtap e accennavo alla pos­sibilità di usarlo per usare il TDD direttamente con il database, sfruttando il fatto che PostgreSQL consente l’uso di transazioni anche per
i comandi di definizione delete database.

Questo è un grosso vantaggio perché posso definire nuovi oggetti dello schema senza che eventuali errori si propaghino al di fuori della ses­sione corrente.

NB: L’esempio che segue è prolisso e probabilmente simile a innumerevoli altri esempi sulle virtù del “test infected” ma mi sembra comunque degno di nota che si possa usare questo approccio anche all’interno di un database. Bene, cominciamo:

\set ECHO
\set QUIET 1
\pset format unaligned
\pset tuples_only true
\pset pager
\set ON_ERROR_ROLLBACK 1
\set ON_ERROR_STOP true
\set QUIET 1

Dopo aver impostato alcuni para­metri per psql, proviamo a definire un nuovo domain che verrà poi utilizzato da una nuova tabella.

Ho installato pgtap in uno schema a sé stante, in modo che le sue funzioni non si mischino con quelle dello schema “vero”.

Visto che vogliamo sviluppare in modalità TDD, creiamo subito il test per verificare la pre­senza del domain, che ovviamente fallirà.

BEGIN;
SET search_path TO tap, public;
SELECT plan(1);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT * FROM finish();

ROLLBACK;

1..1
not ok 1 - verifichiamo la presenza del domain
# Failed test 1: "verifichiamo la presenza del domain"
# Looks like you failed 1 test of 1

Aggiungiamo il domain:

BEGIN;

CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);

SET search_path TO tap, public;
SELECT plan(1);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT * FROM finish();

ROLLBACK;

1..1
ok 1 - verifichiamo la presenza del domain

Funziona! Adesso tocca alla tabella:

BEGIN;

CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);

SET search_path TO tap, public;
SELECT plan(2);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT has_table( 'new_table', 'verifichiamo la presenza di new_table');
SELECT * FROM finish();

ROLLBACK;

1..2
ok 1 - verifichiamo la presenza del domain
not ok 2 - verifichiamo la presenza di new_table
# Failed test 2: "verifichiamo la presenza di new_table"
# Looks like you failed 1 test of 2

Solita storia: facciamo in modo di pas­sare anche questo secondo test.

BEGIN;

CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
CREATE TABLE new_table (col new_domain);

SET search_path TO tap, public;
SELECT plan(2);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT has_table( 'new_table', 'verifichiamo la presenza di new_table');
SELECT * FROM finish();

ROLLBACK;

Aggiungiamo qualche test “perimetrale”:

BEGIN;

CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
CREATE TABLE new_table (col new_domain);

SET search_path TO tap, public;
SELECT plan(4);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT has_table( 'new_table', 'verifichiamo la presenza di new_table');
SELECT col_type_is('new_table', 'col', 'new_domain');
SELECT col_hasnt_default('new_table', 'col');
SELECT * FROM finish();

ROLLBACK;

Uno degli aspetti interes­santi è che pos­siamo testare l’efficacia della validazione del domain separatamente dal suo uso effettivo in qualche tabella. Nell’esempio che segue creiamo una tabella che ha il solo scopo di testare gli insert:

BEGIN;

CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
CREATE TEMP TABLE dummy (col new_domain);

SET search_path TO tap, public;
SELECT plan(3);
SELECT throws_ok(
    'INSERT INTO dummy (col) values(5)',
    '23514',
    'value for domain new_domain violates check constraint "new_domain_check"',
    'Non sono consentiti valori superiori a 4'
);
SELECT throws_ok(
    'INSERT INTO dummy (col) values(0)',
    '23514',
    'value for domain new_domain violates check constraint "new_domain_check"',
    'Non sono consentiti valori inferiori a 0'
);
SELECT lives_ok(
    'INSERT INTO dummy (col) values(1)',
    '1 è un valore consentito'
);
SELECT * FROM finish();

ROLLBACK;

1..3
ok 1 - Non sono consentiti valori superiori a 4
ok 2 - Non sono consentiti valori inferiori a 0
ok 3 - 1 è un valore consentito

Una volta che siamo soddisfatti delle modifiche basterà estrarre gli statement DML e sottometterli, lasciando alla nostra suite di test.

Test-driven design con PostgreSQL: pgTAP

Friday, May 15th, 2009

Fino ad ora ho sempre testato il database attraverso un qualche strumento esterno, come i vari JUnit, TestNG o tcltest.

Sono librerie che funzionano bene e che sono familiari a chiunque pratichi un minimo di TDD ma non particolarmente tagliate per testare i database (con l’eccezione forse di TestNG, che non uso da anni): vuoi perché bisogna pre­vedere delle fixture elaborate che vanno poi eliminate; vuoi perché, a ben guardare, bisogna attraversare tutta una serie di strati software anche solo per verificare un constraint.

Così ho fatto una ricerca e ho trovato due utilità per PostgreSQL — pgTAP e PGUnit — che permettono di creare delle suite di test eseguite direttamente nel database.

I vantaggi sono diversi: oltre all’accesso più diretto e pre­stazioni pre­sumibilmente migliori, la gestione delle fixture risulta semplificata perché basta fare il rollback alla fine dei test; inoltre, dato che in PostgreSQL i comandi DDL sono transazionali, diventa pos­sibile testare le modifiche al database istantaneamente, senza doverlo fare in modo permanente.

Per ora ho scelto pgTAP. Mette a disposizione una serie di funzioni che pos­sono venire molto comode per scrivere i test.

Le prime impres­sioni sono buone e penso che il miglioramento sarà anche più marcato quando potrò disegnare direttamente da zero uno schema in modalità TDD.

Analisi delle prestazioni su Mac OS X 10.5 Server

Friday, June 20th, 2008

Segnalo questo articolo dove sono spiegate diverse strategie per farsi un quadro piuttosto pre­ciso delle pre­stazioni del sistema. Non ho verificato, ma credo che buona parte delle indicazioni siano valide anche per la versione client.

Tool del giorno: IETester

Thursday, May 29th, 2008

Questo farà felici molti sviluppatori: si tratta di un browser che permette di testare, da un’unica applicazione, i motori di rendering di Internet Explorer nelle versioni 8 beta 1, 7, 6 e 5.5, su XP e Vista. #

Utilities del giorno: viewS e Browsershots

Tuesday, November 27th, 2007

Una segnalazione per due utility molto carine.

La prima è viewS, un bookmarklet per visualizzare il sorgente di una pagina html. Non certo una novità sconvolgente ma è ben fatto e indipendente dal browser.

La seconda è browsershots.org, un sito che visualizza uno screenshot di una pagina html con vari browser.

Test Driven Development: suoi vantaggi e un esempio

Saturday, November 3rd, 2007

Il Test Driven Development (TDD) è una metodica di sviluppo in cui il codice viene scritto dopo i test che ne verificano la correttezza. È un requisito fondamentale di sistemi come l’Extreme Programming (XP) o più in generale di metodi di programmazione definiti “agili”.

A mio parere il TDD può essere applicato con successo anche senza sposare in toto la filosofia agile: consente di scrivere codice migliore in ogni caso. E credo che sia una metodica destinata a rimanere in uso ancora a lungo, anche dopo che il fenomeno XP (che rimane secondo me il più developer–friendly) e simili saranno passati.

Una degli aspetti più fruttuosi del TDD è che consente di definire molto pre­cocemente i casi limite del software in sviluppo, soprattutto quelli che sembrano sempre troppo stupidi per essere testati; troppo stupidi adesso, cioè, perché poi, fra un mese o un anno tutte le assunzioni tacite che avevamo ben chiare in testa saranno dimenticate e allora quei test, troppo stupidi perché vales­sero la pena di essere scritti, ci saranno di grande aiuto.

A chi avesse bisogno di un esempio semplice e pratico su come funziona il TDD, consiglio questo post di Brian Button.

La Singolarità vista da un “collaudatore”

Tuesday, October 2nd, 2007

Il titolo è un po’ criptico, lo so! Cominciamo quindi con il definire i termini della questione, in particolare per il concetto di “Singolarità”.

Per Singolarità – o, più pre­cisamente, Singolarità Tecnologica (ST) – si intende grosso modo il momento in cui, grazie ai progressi tecnologici, sarà pos­sibile creare un’intelligenza superiore a quella umana. Dico “grosso modo” perché in realtà sono state date diverse definizioni di questo concetto, tutte di sapore più o meno profetico e misticheggiante.

James Bach (il “collaudatore” del titolo), in questo post, getta un bel po’ di acqua sul fuoco, mettendo in rilievo tutta una serie di appros­simazioni che rendono tutta la questione molto meno eccitante. Ad esempio, non siamo in grado nemmeno di definire l’intelligenza: come potremo sapere di averla superata? La pre­visione di Bach è che il raggiungimento della ST verrà vanificato dalla sua crescente comples­sità e fragilità.

Ho sempre pensato che il fattore limitante della comples­sità fosse quello più “pesante”. Tuttavia, oggi ne sono meno sicuro: non ci sarà una trasumanazione come quella sognata dai futurologi ma credo che anche uno – giustamente – scettico come Bach sarà sorpreso dei progressi che saranno stati fatti.

« Voci Precedenti