Visualizzazione post con etichetta xp. Mostra tutti i post
Visualizzazione post con etichetta xp. Mostra tutti i post

sabato 2 febbraio 2008

Torneo Refactoring

E' partito un torneo di refactoring.

Non partecipando, comunque ho deciso di affrontare il problema, per il momento commento come avrei affrontato la prova01:

Il codice e' versionato nel repos. aziendale.

prima di fare il refactoring richiesto, che chiede di sfruttare la somiglianza tra evaluate e forEachDo, creo dei test appositi per essi, e poi rifattorizzo, e verifico che i test passano anche dopo il refactoring.

Per testare la forEachDo:
mi appoggio ad una implementazione di Block che implementa la evaluate concatenando i toString degli oggetti processati in una unica stringa globale.
Se applico la evaluate con questa implementazione di Block, ad una collezione contenente "first" e "second" mi aspetto che dopo il test la stringa globale abbia concatenato "first" e "second".

public class BlockImplForTest implements Block {
public static String references="";
public static void reset() {
references="";
}

public void evaluate(Object object)
{
references+=object.toString();
}
}

Il test e' il seguente:

@Test
public void testSelect() {
OrderedCollection col = new OrderedCollection();
col.add("first");
col.add("second");

Block stringRefAppender=getFreshBlockImplForTest();
col.forEachDo(stringRefAppender);

assertEquals(BlockImplForTest.references,"first"+"second");
assertFalse(BlockImplForTest.references.equals("wrong"));

}

Per rendere piu' evidente che Il Block che uso deve essere "fresco", lo reperisco tramite questo metodo che fa anche un reset dello stato:

BlockImplForTest getFreshBlockImplForTest() {
BlockImplForTest.reset();
return new BlockImplForTest();
}

Forse, come design, e' discutibile aver usato una variabile globale, ma allo scopo del test va piu' che bene.

Il test del select crea una classe anonima che implementa il PredicateBlock in modo che che la is restituisca true solamente se la stringa vale "second".
In questo modo, mi aspetto che la lista filtrata attraverso questo PredicateBlock contenga solamente la stringa second. Sfrutto parte della logica del precedente test per scandagliare la collezione restituita e verificare che contenga solamente "second".

@Test
public void testSelector() {
OrderedCollection col = new OrderedCollection();
col.add("first");
col.add("second");

Block stringRefAppender=getFreshBlockImplForTest();

assertEquals("",BlockImplForTest.references);
PredicateBlock getOnlySecond= new PredicateBlock()
{
public boolean is(Object object) {
return "second".equals(object);
}

};

OrderedCollection ordCol = col.select(getOnlySecond);
ordCol.forEachDo(stringRefAppender);
assertEquals("second",BlockImplForTest.references);

}

Il test ci assicura che l'attuale implementazione dei metodi di OrderCollection fanno quello che ci aspettiamo, ed ora la si rifattorizza cercando di sfruttare la somiglianza tra forEachDo e select.
Entrambe eseguono la scansione della collezione, solo che una esegue una certa operazione, e lo fa su tutti gli elementi, senza restituire nulla, l'altra restituisce una sottocollezione di tutti i membri della collezione che soddisfano un certo predicato.

Ragionando un attimo, dal punto di vista logico possono essere due casi particolari di una unica operazione che scandisce gli elementi , valuta il predicato, esegue l'operazione ed (eventualmente) restituisce gli elementi processati.

Il caso particolare della forEachDo e' che il predicato restituisce sempre true, che non ci interessa utilizzare il valore restituito, mentre il caso particolare della select e' che la evaluate non esegue nulla.

La rifattorizziamo dunque creando due implementazioni di Block e PredicateBlock in accordo con questi due casi particolari:

1) Creiamo il Block e il PredicateBlock corrispondente ai casi particolari appena discusi:

private static final PredicateBlock passesAll =
new PredicateBlock() {
public boolean is(Object object) {
return true;
}
};

private static final Block neuterBlock =
new Block() {
public void evaluate(Object object) { }
};


2) Aggiungiamo un metodo "evaluate and select":

private OrderedCollection evaluateAndSelect(PredicateBlock pb, Block b)
{
OrderedCollection result = new OrderedCollection();
Iterator iterator = _items.iterator();
while(iterator.hasNext()) {
Object object = iterator.next();
if (pb.is(object)) {
b.evaluate(object);
result.add(object);
}
}
return result;
}

3) Trasformiamo la ForEach e la Select in modo che siano appunto casi particolari dell'utilizzo di evaluateAndSelect sfruttando le implementazioni "neutre" di Block ed PredicateBlock:

public OrderedCollection select(PredicateBlock aBlock) {
return evaluateAndSelect(aBlock,neuterBlock);
}

public void forEachDo(Block aBlock) {
evaluateAndSelect(passesAll,aBlock);
}

lunedì 26 novembre 2007

la qualità non è negoziabile

Nel suo libro relativo alla sua esperienza di Scrum e di Xp, Eric Kniberg spiega che il loro approccio nello sviluppo tra i vari principi adotta quello che la "qualità non è negoziabile".
Più precisamente si distingue tra qualità esterna e qualità interna.
La qualità esterna può essere rappresentata da una interfaccia utente scarsa, povera, anti-intuitiva, mentre la qualità interna può essere rappresentata da un codice robusto, ben testato, privo di bachi, manutenibile, etc...

La qualità esterna può essere considerata semplicemente parte dello scope, mentre la qualità interna dovrebbe essere un must non negoziabile.

La qualità percepibile è quella esterna, dunque si potrebbe essere tentati ad essere accomodanti a riguardo della qualità interna, ed accettare da parte del committente discorsi del tipo "rispetto il fatto che tu stimi che questa attività possa essere valutata in 6 story-points, ma sono sicuro che con un quick fix potresti trasformarla in una attività valutabile in soli 2 story-points, [tanto l'utente non se ne accorge]".

Questo discorso andrebbe respinto nei confronti del committente, anche se nella pratica questo è molto difficile, perché ciò che si vende è ciò che s vede.

Dunque è difficile dare il giusto valore alla qualità interna.

Se ne discute in un tread della lista "scrumdevelopment".

sabato 20 ottobre 2007

httpxmlfixture

L'ultima cosa di cui mi sono occupato, per lavoro, è la creazione di una fixture per fitnesse, per fare acceptance testing e test driven development di applicazioni web.

Si tratta praticamente di una evoluzione di questa fixture che consente di fare alcune verifiche basate su xpath rispetto al contenuto di una pagina web, ovvero verificare che un certo nodo, individuato via espressione xpath, sulla pagina indicata da una certa url esista o meno, che abbia un certo valore, oppure che il numero di nodi che "matchano" l'espressione sia quello che ci si aspetta.

A questo ho aggiunto le seguenti caratteristiche:

- poter accedere a pagine che richiedono autenticazione (basic o, in alcuni casi, form)
- poter lavorare anche su pagine che non sono ben formate come xml, usando jTidy come preprocessor
- gestire i cookies così potendo gestire anche la sessione di navigazione (se basata su cookie)
- gestire eventuale submission di parametri via post o get.
- poter generare automaticamente un test che va a buon fine rispetto ad un esempio statico.

In teoria dovrebbe essere sufficiente per poter fare dei chiari test di accettazione per applicazioni web, almeno se si tratta di testare solamente cose "server side" (niente javascript/ajax quindi).

Per esempio: il customer si dice soddisfatto che l'applicazione renderizzerà una pagina in modo conforme ad un certo esempio fornito come pagina statica. A quel punto la pagina statica viene "processata" da xml2xpath, e viene creato il relativo test che ha successo quando la pagina viene "deployata".

Supponiamo che nella pagina vi sia una tabella, che dovrà poi contenere dati dinamici.
Il customer vorrà magari che "in quella tabella poi dovranno essere presenti i dati che verranno inseriti in una certa form".
Allora si modifica subito l'espressione xpath che verifica i dati in tabella, e si fa in modo che il test sia esteso in modo da incluere anche l'azione di "inserire un certo dato, e poi andare nella pagina dell'elenco ed aspettarsi di trovare quel dato".

La cosa descritta adesso è più o meno una user story e l'idea è di scrivere un test che la "descrive", e ne verifica anche la corretta implementazione.

Un po' più in dettaglio se la pagina statica pensata all'inizio è del tipo:


<html>
<head/>
<body>
<table>
<tr>
<td>entry1</td>
</tr>
<tr>
<td>entry2</td>
</tr>
<tr>
<td>entry3</td>
</tr>
</table>
</body>
</html>



allora lo stylesheet xml2xpath genera il seguente pezzo di tabella di test:

| Value | /html/body/table/tr/td | entry1 |
| Value | /html/body/table/tr[2]/td | entry2 |
| Value | /html/body/table/tr[3]/td | entry3 |

a questo punto, se si progetta che l'azione di inserimento di questi dati dovrà essere data da una certa jsp che si chiamerà submitaction.jsp, con un parametro entryname, allora, prendendo un po' spunto dal test precedente, l'intera storia "sottometti il dato entryX e poi verifica che
visualizzando la tabella, esso sia presente", è un po' come segue":

...
|newUrlPost|http://myserver/submitaction.jsp|
|NameValuePair|entryname|entryX|
|newUrlGet|http://myserver/visualize.jsp |
|AValue|/html/body/table/tr[*]/td | entryX |

Ovviamente poi il test resta lì utile anche a verificare non solo quando l'implementazioe è corretta, ma anche che continui a rimanere corretta.

Informazioni personali

La mia foto
I have been coding from the old C64 times. Studied Computer Sciences at Milan University. I also worked there in technical operations. Many years of experiences in coding Java and C#, desktop and web applications, with practices like unit testing. I used to play with 3d graphics in architecture recently with Blender 3d. Now I look for support related to some projects I am working on, oriented in automation in tourism related services, using functional programming framework, specifically F# and Suave.IO. email
tonyx1 (at) gmail.com github https://github.com/tonyx