PDA

View Full Version : Mi sono esercitato :D (NullSound inside)


AnonimoVeneziano
25-01-2008, 13:11
Ho deciso di esercitarmi un po' stamattina prima di studiare (:muro:) in questo Test-driven programming che tanto è affascinante quanto sconosciuto alle mie meningi :D

Così ho deciso di creare l'infausta "NullSound" che manca per risolvere il bug del crash col device audio occupato.

Ecco cos'ho fatto.

Come fek mi ha insegnato ho creato due test paletto che segnano la fine del task (ammetto di averli scopiazzati un po' su quelli della NullAudio) :


TestEnvironment.java

public void testSoundCreationFromNullAudio()
{
AudioFactory audioFactory = new FailingAudioFactoryMock();

environment.createAudio(audioFactory);
Sound sound = environment.getAudio().createSound("diamond");
assertTrue(sound.isNull());
}


public void testSoundCreationFromNonNullAudio()
{
AudioFactory audioFactory = new MockAudioFactory();

environment.createAudio(audioFactory);
Sound sound = environment.getAudio().createSound("diamond");
assertFalse(sound.isNull());
}


Dopo aver constatato che fallissero (cum sommo gaudio :D ) ho proseguito cercando di pensare al design di quello che volevo fare (riprendendo un po' anche quella che è la NullAudio) e così ho creato un nuovo file di test per i test sulla NullSound aggiungendo questi test :


public class TestNullSound extends TestCase
{

public void testNullSound()
{
Sound sound = new NullSound();
assertTrue(sound.isNull());
}

public void testNullSoundPlayed()
{
Sound sound = new NullSound();
assertFalse(sound.wasPlayed());
sound.play();
assertTrue(sound.wasPlayed());
}

public void testNullSoundPlayedReset()
{
Sound sound = new NullSound();
sound.play();
sound.reset();
assertFalse(sound.wasPlayed());
}

public void testNullSoundPlayedFreeMemoryReset()
{
Sound sound = new NullSound();
sound.play();
sound.freeMemory();
assertFalse(sound.wasPlayed());
}

public void testReturnNullSoundNameNull()
{
Sound sound = new NullSound();
sound.getName().equals("null");
}

}


Ovviamente i test li ho aggiunti uno per uno e ho cercato di farli passare nel modo più semplice possibile (mi rendo conto che però questa cosa funzioni molto meglio in Pair, perchè se la stessa persona fa sia tests che implementazione un po' rimane influenzata ...)

Il risultato di tutta questa trafila di tests , oltre all'aggiunta della "isNull()" in Sound.java, OpenALSound.java, MockSound.java, è stata la creazione di una classe NullSound.java che ha questo codice :


public class NullSound implements Sound
{
private boolean wasPlayed = false;

public void freeMemory()
{
wasPlayed = false;
}


public Object getName()
{
return "null";
}


public void play()
{
wasPlayed = true;
}


public void reset()
{
wasPlayed = false;
}


public boolean wasPlayed()
{
return wasPlayed;
}

public boolean isNull()
{
return true;
}

}


Ho cercato , attraverso di i test, di creare un design per la NullSound che emulasse il più possibile il comportamento di una Sound non-null (mantenendo la variabile boolean wasPlayed per esempio), non so se questo è giusto come concetto, però almeno l'ho fatto attraverso i tests :D.

Alla fine ho scommentato i due tests finali che avevo creato e lanciandoli ho constatato che non fallivano ... questo immagino significhi che ho fatto qualcosa di ridondante , vero? :mbe:

Il risultato finale è comunque che il programma non crasha più.

Non ho committato, aspetto i commenti di chi è più esperto con il test-driven che mi dica se ho fatto cavolate :D

Ciao

fek
25-01-2008, 13:20
Alla fine ho scommentato i due tests finali che avevo creato e lanciandoli ho constatato che non fallivano ... questo immagino significhi che ho fatto qualcosa di ridondante , vero? :mbe:

No, significa che hai finito il task. Se l'Ant build funziona e non ci sono errori puoi committare.

In due ore ieri sera ho creato un mostro :D

AnonimoVeneziano
25-01-2008, 13:21
In due ore ieri sera ho creato un mostro :D

:asd:

Bonfo
26-01-2008, 22:40
:asd:

Benvenuto nel mondo dei malati di TDD!

71104
26-01-2008, 23:50
c'è qualcosa che non mi convince :mbe:
in Sound e derivate i metodi getName e isNull vengono usati solo dai test; stessa cosa per i metodi isCreated e isNull di Audio e derivate.
dobbiamo eliminarli? :mbe:
e se proprio dobbiamo tenerli io francamente quel getName lo convertirei in toString, per amor di Java ^^
inoltre in Audio e derivate c'era un metodo isInitialised (scritto pure male visto che si dice initialized :D) che non era usato proprio da nessuno, ne' test ne' codice; l'ho eliminato.

71104
26-01-2008, 23:54
segnalo altri metodi utilizzati esclusivamente dai test: Sound.wasPlayed, Audio.isMusicPlaying.

fek, in generale dicci come dobbiamo comportarci quando vediamo situazioni simili (metodi usati solamente dai test). io sono per l'eliminazione e il testing di quelle funzionalità in altre maniere :)
sono dell'idea che il TDD non debba causare l'aggiunta di codice che non sarebbe stato messo in un ipotetico sviluppo untested, perché altrimenti significa che il TDD incrementa la quantità di codice causando danni oltre che benefici. il massimo che si potrebbe accettare sono metodi come i vari createForTesting statici (sono delle sorte di costruttori alternativi).

AnonimoVeneziano
27-01-2008, 02:16
segnalo altri metodi utilizzati esclusivamente dai test: Sound.wasPlayed, Audio.isMusicPlaying.

fek, in generale dicci come dobbiamo comportarci quando vediamo situazioni simili (metodi usati solamente dai test). io sono per l'eliminazione e il testing di quelle funzionalità in altre maniere :)
sono dell'idea che il TDD non debba causare l'aggiunta di codice che non sarebbe stato messo in un ipotetico sviluppo untested, perché altrimenti significa che il TDD incrementa la quantità di codice causando danni oltre che benefici. il massimo che si potrebbe accettare sono metodi come i vari createForTesting statici (sono delle sorte di costruttori alternativi).

I metodi Sound.wasPlayed e Audio.isMusicPlaying erano già presenti prima di queste aggiunte. Sinceramente non so perchè siano stati messi. Probabilmente erano stati messi in previsione di qualche uso futuro? O sono stati introdotti solo attraverso il TDD? (Come la isNull()) .

Comunque credo già di sapere la risposta di Fran (che ci arriverà dopo il fine settimana). Penso lui preferisca avere tutte le classi perfettamente testate e coi metodi di test piuttosto che classi più "economiche" in fatto di metodi , ma meno testate, o che comunque iniziano a creare eccezioni dalla via del TDD. Penso che la paura sia che se si inizia con le eccezioni al TDD alla ricerca di un improbabile incremento dell'efficienza si possa finire nel baratro , come tra l'altro mi sembra sia già successo in passato al progetto.

Dopotutto si parla dell'aggiunta di qualche metodo che alla fine non viene mai chiamato nel codice di produzione e che quindi il compilatore dinamico non perde tempo a compilare in fase di esecuzione. L'unico svantaggio può essere l'incremento della dimensione dei .class , ma visti i tipi di funzioni (più che altro getter) direi che l'incremento può essere al massimo di qualche kilobyte. Quindi IMHO non vale la pena cercare di piallare via questi metodi, che tra l'altro si potrebbero anche rivelare utili in futuro e caratterizzano bene gli oggetti in questione (chissà quando potrebbe essere necessario sapere se la musica è in riproduzione o se l'oggetto Audio o Sound instanziato è null o meno).

Ciao

cdimauro
27-01-2008, 06:41
TUTTI i metodi usati in produzione DEVONO avere ALMENO un test che li eserciti e ben più d'uno nel caso in cui abbiano parametri e/o dipendenze da altri valori, come variabili d'istanza o di classe: si deve, insomma, formalizzare il "contratto" fra chiamante e chiamato.

Se ci sono metodi NON di test (quindi esposti pubblicamente) e NON usati nel codice di produzione io sono per la loro eliminazione seduta stante. YAGNI!

71104
27-01-2008, 11:15
TUTTI i metodi usati in produzione DEVONO avere ALMENO un test che li eserciti e ben più d'uno nel caso in cui abbiano parametri e/o dipendenze da altri valori, come variabili d'istanza o di classe: si deve, insomma, formalizzare il "contratto" fra chiamante e chiamato.

Se ci sono metodi NON di test (quindi esposti pubblicamente) e NON usati nel codice di produzione io sono per la loro eliminazione seduta stante. YAGNI! e per quanto riguarda i metodi non di produzione utilizzati però nei test?

71104
27-01-2008, 11:17
Dopotutto si parla dell'aggiunta di qualche metodo che alla fine non viene mai chiamato nel codice di produzione e che quindi il compilatore dinamico non perde tempo a compilare in fase di esecuzione. anche se quei metodi non li usa nessuno comunque nel file .class ci devono stare. fai un esperimento: apri NullAudio.class con un editor esadecimale e cerca la stringa testuale "isNull" :)

AnonimoVeneziano
27-01-2008, 12:17
anche se quei metodi non li usa nessuno comunque nel file .class ci devono stare. fai un esperimento: apri NullAudio.class con un editor esadecimale e cerca la stringa testuale "isNull" :)


E infatti io ho scritto che l'unico svantaggio può essere l'aumento della dimensione dei file .class AL MAX di qualche KB (tutti insieme intendo :D ) , ma che questo non significa necessariamente un decremento prestazionale. Cioè, se il codice non viene eseguito il compilatore dinamico (il JIT) non si preoccupa della compilazione di quel codice che quindi, in fase di esecuzione, è come se non esistesse . Anche se questo decremento ci fosse probabilmente sarebbe del tutto ininfluente a livello pratico.

Quei metodi però hanno il merito di specificare con chiarezza il contratto della classe e di specificare quindi le funzionalità che questa deve avere. Insomma, non credo valga la pena toglierli, inoltre mi sembra che nei test vengano usati (perlomeno isMusicPlaying() di AudioInterface che è l'unico che mi ricordo così sull'unghia)

Ciao

71104
27-01-2008, 12:24
ricordo ancora la prima volta che io scrissi dei metodi che rimasero utilizzati solo dai test: era il task per l'implementazione della classe che mostra i punteggi. fek mi disse di togliere (o rendere privati) quei metodi e di testare in altra maniera; quella volta imparai cos'è il Black Box Testing.

altra cosa che ho imparato: mai cercare di indovinare le decisioni di fek :asd:
per questo gli ho anche chiesto in generale cosa fare in situazioni del genere, così diventiamo autonomi e non dobbiamo più aspettare che lui legga il forum; anche se in realtà, memore di quando scrissi la famosa classe Number, credo di sapere cosa risponderà.

71104
27-01-2008, 12:37
http://www.hwupgrade.it/forum/showpost.php?p=10258844&postcount=74

terza quotatura. che bei ricordi :cry: :D

AnonimoVeneziano
27-01-2008, 15:55
altra cosa che ho imparato: mai cercare di indovinare le decisioni di fek :asd:


Ne prendo atto :asd:

cdimauro
27-01-2008, 20:35
e per quanto riguarda i metodi non di produzione utilizzati però nei test?
Mi allineo a quanto già scritto da Fran (e che hai riportato qualche messaggio dopo): vanno testati soltanto quelli delle interfacce pubbliche.

Gli altri metodi (non di produzione) teoricamente non dovrebbero nemmeno esistere, per cui o si trova il modo di toglierli di mezzo, oppure dovrebbero stare soltanto sugli oggetti di test.

Sempre se possibile, eh!

thebol
29-01-2008, 12:01
se un metodo pubblico non di produzione rende (molto) più semplice scrivere i test per me può rimanere. Ad esempio testare un componente che controlla qualcosa di basso livello(che si assume funzioni) andando a controllare questo componente cosa fà(scrive sul buffer audio della scheda audio?) non è sempre obbligatorio.

Certo si può fare con un mock, passando il mock al nostro componente, e verificare che faccia le chiamate giuste(ci sarà il mock ad ascoltarle). Ma non è piu blackboxtesting, e secondo me ci si lega troppo all'implementazione.(può comunque valer la pena farlo in certe occasioni, magari per test di integrazione).

fek
29-01-2008, 20:30
Comunque credo già di sapere la risposta di Fran (che ci arriverà dopo il fine settimana). Penso lui preferisca avere tutte le classi perfettamente testate e coi metodi di test piuttosto che classi più "economiche" in fatto di metodi , ma meno testate, o che comunque iniziano a creare eccezioni dalla via del TDD. Penso che la paura sia che se si inizia con le eccezioni al TDD alla ricerca di un improbabile incremento dell'efficienza si possa finire nel baratro , come tra l'altro mi sembra sia già successo in passato al progetto.

Il principio primo e' raggiungere il 100% di codice coperto da test. Quindi tutte le classi vanno testate e non c'e' alcuna eccezione (a parte ovviamente classi che non possono essere fisicamente testate perche' interfaccia verso librerie come OpenGL).

Il secondo principio e' l'interfaccia pubblica di una classe dev'essere il piu' semplice e minimale possibile: quindi se si puo' eliminare un metodo dall'interfaccia pubblica in maniera semplice, va fatto (senza infrangere il principio primo ovviamente).

Il terzo principio: non mi interessa alcuna considerazione prestazionale fino a che qualcuno non mi porta dati concreti che mostrano un problema concreto. E dubito fortemente questo accadra' in Diamonds.