Mondi su mondi, sistemi di sistemi.

Utility del giorno: speedlimit

Monday, September 13th, 2010

Uno dei test che si fa rara­mente è quello di simu­lare il com­por­ta­mento di un sito o dell’attività di rete sulla WAN, in modo da avere un’idea suf­fi­cien­te­mente pre­cisa di come giri con una con­nes­sione in edge, ad esempio.

In realtà, la cosa non è dif­fi­cile se si è dispo­sti a spor­carsi le mani con il firewall. Per i pigri come me, però, c’è speed­li­mit: un pan­nello delle pre­fe­renze con cui impo­stare i para­me­tri che ci ser­vono, ovvero, per quali porte e per quali host limi­tare la banda ad un certo valore appli­cando anche un ritardo sulla comu­ni­ca­zione a piacere.

Utility del giorno: Fake

Monday, July 12th, 2010

Una delle cose più noiose è il dover ripe­tere manual­mente una serie di ope­ra­zioni con il bro­w­ser: per veri­fi­care che tutto sia a posto; come test per la gui; nell’accesso a ser­vizi che richie­dono la ripe­ti­zione di una serie sem­pre uguali di passi.

Ci sono già stru­menti di auto­ma­zione e replay come Selenium, ma sono pen­sati esclu­si­va­mente per il testing.

Fake è un’applicazione basata su Safari che copre sia gli sce­nari più stan­dard di Selenium sia i casi in cui vogliamo auto­ma­tiz­zare l’accesso noto­ria­mente imper­vio a siti come quelli di home ban­king e simili. In più, Fake per­mette alcune ope­ra­zioni molto più potenti e che non hanno nulla a che vedere con il testing ma che pos­sono essere uti­lis­sime come l’esecuzione di script di shell o AppleScript.

L’interfaccia è un incro­cio fra Automator e Safari ed è di una faci­lità estrema: basta tra­sci­nare le azioni nella time­line e impo­stare i para­me­tri. Una volta messa a punto la sequenza che vogliamo, pos­siamo sal­varla per il futuro.

Progettare al buio?

Friday, October 30th, 2009

Dato che lavoro pra­ti­ca­mente solo in ambito web mi capita a volte di pen­sare con un po’ d’invidia a chi svi­luppa appli­ca­zioni desk­top, dove ha a dispo­si­zione stru­menti come l’Interface Builder.

“Usa i dati, Luke…”

Stamattina, però, sen­tendo “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 sem­pre più legati a fat­tori quan­ti­ta­tivi (spe­ri­men­tali, quasi) che deri­vano dall’analisi dei dati degli utenti.

According to Gemmell

Viceversa, nel pod­cast, Matt Gemmell dà una serie di con­si­gli – uti­lis­simi, peral­tro – sull’usabilità delle appli­ca­zioni desk­top e mi sono reso conto che non esi­stono stru­menti ana­li­tici come sul web; non che io sap­pia, per­lo­meno. È vero che la situa­zione è diversa, a par­tire dagli ovvi pro­blemi di pri­vacy, ma credo la cosa sarebbe fattibile.

Ad esem­pio, sarebbe uti­lis­simo sapere quali sono le fun­zioni più usate; come ven­gono impo­state le pre­fe­renze e così via. Possibile che non ci abbia mai pen­sato nessuno?

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

Wednesday, July 29th, 2009

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.

pgTAP: un esempio concreto di TDD

Wednesday, May 27th, 2009

Qualche giorno fa par­lavo di pgtap e accen­navo alla pos­si­bi­lità di usarlo per usare il TDD diret­ta­mente con il data­base, sfrut­tando il fatto che PostgreSQL con­sente l’uso di tran­sa­zioni anche per
i comandi di defi­ni­zione delete database.

Questo è un grosso van­tag­gio per­ché posso defi­nire nuovi oggetti dello schema senza che even­tuali errori si pro­pa­ghino al di fuori della ses­sione corrente.

NB: L’esempio che segue è pro­lisso e pro­ba­bil­mente simile a innu­me­re­voli altri esempi sulle virtù del “test infec­ted” ma mi sem­bra comun­que degno di nota che si possa usare que­sto approc­cio anche all’interno di un data­base. 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 impo­stato alcuni para­me­tri per psql, pro­viamo a defi­nire un nuovo domain che verrà poi uti­liz­zato da una nuova tabella.

Ho instal­lato pgtap in uno schema a sé stante, in modo che le sue fun­zioni non si mischino con quelle dello schema “vero”.

Visto che vogliamo svi­lup­pare in moda­lità TDD, creiamo subito il test per veri­fi­care la pre­senza del domain, che ovvia­mente 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 sto­ria: fac­ciamo in modo di pas­sare anche que­sto 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 qual­che 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 inte­res­santi è che pos­siamo testare l’efficacia della vali­da­zione del domain sepa­ra­ta­mente dal suo uso effet­tivo in qual­che 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 sod­di­sfatti delle modi­fi­che basterà estrarre gli sta­te­ment DML e sot­to­met­terli, lasciando alla nostra suite di test.

Test-driven design con PostgreSQL: pgTAP

Friday, May 15th, 2009

Fino ad ora ho sem­pre testato il data­base attra­verso un qual­che stru­mento esterno, come i vari JUnit, TestNG o tcl­test.

Sono libre­rie che fun­zio­nano bene e che sono fami­liari a chiun­que pra­ti­chi un minimo di TDD ma non par­ti­co­lar­mente tagliate per testare i data­base (con l’eccezione forse di TestNG, che non uso da anni): vuoi per­ché biso­gna pre­ve­dere delle fix­ture ela­bo­rate che vanno poi eli­mi­nate; vuoi per­ché, a ben guar­dare, biso­gna attra­ver­sare tutta una serie di strati soft­ware anche solo per veri­fi­care un constraint.

Così ho fatto una ricerca e ho tro­vato due uti­lità per PostgreSQL — pgTAP e PGUnit — che per­met­tono di creare delle suite di test ese­guite diret­ta­mente nel database.

I van­taggi sono diversi: oltre all’accesso più diretto e pre­sta­zioni pre­su­mi­bil­mente migliori, la gestione delle fix­ture risulta sem­pli­fi­cata per­ché basta fare il roll­back alla fine dei test; inol­tre, dato che in PostgreSQL i comandi DDL sono tran­sa­zio­nali, diventa pos­si­bile testare le modi­fi­che al data­base istan­ta­nea­mente, senza doverlo fare in modo permanente.

Per ora ho scelto pgTAP. Mette a dispo­si­zione una serie di fun­zioni che pos­sono venire molto comode per scri­vere i test.

Le prime impres­sioni sono buone e penso che il miglio­ra­mento sarà anche più mar­cato quando potrò dise­gnare diret­ta­mente da zero uno schema in moda­lità TDD.

Analisi delle prestazioni su Mac OS X 10.5 Server

Friday, June 20th, 2008

Segnalo que­sto arti­colo dove sono spie­gate diverse stra­te­gie per farsi un qua­dro piut­to­sto pre­ciso delle pre­sta­zioni del sistema. Non ho veri­fi­cato, ma credo che buona parte delle indi­ca­zioni siano valide anche per la ver­sione client.

Tool del giorno: IETester

Thursday, May 29th, 2008

Questo farà felici molti svi­lup­pa­tori: si tratta di un bro­w­ser che per­mette di testare, da un’unica appli­ca­zione, i motori di ren­de­ring di Internet Explorer nelle ver­sioni 8 beta 1, 7, 6 e 5.5, su XP e Vista. #

Utilities del giorno: viewS e Browsershots

Tuesday, November 27th, 2007

Una segna­la­zione per due uti­lity molto carine.

La prima è viewS, un book­mar­klet per visua­liz­zare il sor­gente di una pagina html. Non certo una novità scon­vol­gente ma è ben fatto e indi­pen­dente dal browser.

La seconda è browsershots.org, un sito che visua­lizza uno screen­shot 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 meto­dica di svi­luppo in cui il codice viene scritto dopo i test che ne veri­fi­cano la cor­ret­tezza. È un requi­sito fon­da­men­tale di sistemi come l’Extreme Programming (XP) o più in gene­rale di metodi di pro­gram­ma­zione defi­niti “agili”.

A mio parere il TDD può essere appli­cato con suc­cesso anche senza spo­sare in toto la filo­so­fia agile: con­sente di scri­vere codice migliore in ogni caso. E credo che sia una meto­dica desti­nata a rima­nere in uso ancora a lungo, anche dopo che il feno­meno XP (che rimane secondo me il più developer–friendly) e simili saranno passati.

Una degli aspetti più frut­tuosi del TDD è che con­sente di defi­nire molto pre­co­ce­mente i casi limite del soft­ware in svi­luppo, soprat­tutto quelli che sem­brano sem­pre troppo stu­pidi per essere testati; troppo stu­pidi adesso, cioè, per­ché poi, fra un mese o un anno tutte le assun­zioni tacite che ave­vamo ben chiare in testa saranno dimen­ti­cate e allora quei test, troppo stu­pidi per­ché vales­sero la pena di essere scritti, ci saranno di grande aiuto.

A chi avesse biso­gno di un esem­pio sem­plice e pra­tico su come fun­ziona il TDD, con­si­glio que­sto post di Brian Button.

« Voci Precedenti