test driven development for ios

54
Test Driven Development for iOS Alessandro Ceseno contact me, my website is: www.alexceseno.it Add me on Linkedin: http://www.linkedin.com/in/alessandroceseno Twitter: https://twitter.com/AlexCeseno

Upload: alessandro-ceseno

Post on 22-Jan-2018

270 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Test Driven Development for iOS

Test Driven Development for iOS

Alessandro Ceseno

contact me, my website is: www.alexceseno.itAdd me on Linkedin:

http://www.linkedin.com/in/alessandrocesenoTwitter:

https://twitter.com/AlexCeseno

Page 2: Test Driven Development for iOS

A chi è rivolto?Programmatori che vogliono sapere cosa è il TDD

Programmatori che vogliono iniziare a sviluppare in iOS

Persone che vogliono conoscere le fondamenta del TDD

Programmatori che vogliono capire un modello di sviluppo software utilizzato nelle metodologie di sviluppo agili

Conoscere ed imparare ad applicare una metodologia di sviluppo software tra le più efficaci

2

Page 3: Test Driven Development for iOS

Perchè il Test Driven Development?Individua in breve tempo le parti di codice che hanno un comportamento corretto

Sviluppo di codice funzionante

Consente lo sviluppo di Clean Code

Consente il test dei singoli componenti software

Confidenza nel codice (tecnica di sviluppo incrementale)

Confidenza nella fase di refactoring

Consente un miglioramento del design per la fase di refactoring3

Page 4: Test Driven Development for iOS

Perchè il Test Driven Development? (2)Consente il rilascio di feature funzionanti

Requisiti e design prima del codice nella mente dello sviluppatore

Small step consentono lo sviluppo di codice funzionante

Si dedica meno tempo al bug fixing

4

Page 5: Test Driven Development for iOS

Cosa è il TDD?È una tecnica di sviluppo software effettuata mediante la scrittura di test che precede lo sviluppo della funzionalità applicativa.

Non è una tecnica di testing.

I test possono essere verificati: barra verde.

I test possono essere non verificati: barra rossa.

5

Page 6: Test Driven Development for iOS

Il ciclo del TDD:

● Aggiungi un piccolo test (Scrivi il test: barra rossa)● Verifica che fallisce il test eseguendo tutti i test e non solo il test che hai

appena scritto● Scrivi l’implementazione per far passare il test (barra verde)● Esegui tutti i test e verifica che abbiano successo● Refactor: rimuovi la duplicazione

6

Page 7: Test Driven Development for iOS

Le tre regole del TDD:

● Non scrivere codice di produzione finchè non hai un test che fallisce

● Non ti è consentito di scrivere più di un test di unità che può fallire

● Non ti è consentito di scrivere più codice di produzione di quello che sia sufficiente per far passare il test che fallisce

7

Page 8: Test Driven Development for iOS

Patter per scrivere un test:

● Organizza: create some objects, collaborators

● Esegui: stimola l’oggetto sotto test

● Assert: controlla i risultati

8

Page 9: Test Driven Development for iOS

Alcuni TDD PATTERN

● Test

I test che scriviamo dovrebbero essere eseguiti il più velocemente possibile.Scrivi una lista di tutti i test che sai che andrai a scrivere.Prima di scrivere il test, chiediti: che cosa deve essere testato?Lascia la postazione sempre in barra verde.Il test first consente la riduzione dello stress.

● Isolated test

I test devono essere isolati: quando un test ha successo oppure un test fallisce questo dovrebbe essere irrelevante per gli altri test.

9

Page 10: Test Driven Development for iOS

● Assert FirstQuando devi scrivere gli assert? Prova a scriverli come prima cosa quando scrivi un test. Spesso è comodo utilizzare una espressione booleana per il test oppure l’equal di due oggetti.● da dove inizi a costruire il sistema● quale è la cosa che devi scrivere per prima per avere la funzionalità minima● pensa e scrivi il test che vuoi che passi come “codice finale”

10

Page 11: Test Driven Development for iOS

● Started Test

Risolvi un problema alla volta e chiediti:“Quest’altro test è ovvio? Ecco quest’altro test sono in grado di implementarlo”.Inizi a scrivere il test e chiediti, quali operazioni devi fare? Quali sono i corretti input? Quali sono i corretti output?Ricorda che il feedback non deve durare troppo tempo. Il feedback è uno dei valori di XP. Utilizza per i test i dati più realistici possibili, quelli che si avvicinano il più possibile ai casi di utilizzo.Mi chiedo l’output che voglio e l’input che voglio. L’input dovrebbe essere il più semplice possibile.

11

Page 12: Test Driven Development for iOS

● Learning test

Utilizza il learning test per imparare una nuova libreria o un nuovo metodo. Scrivi un piccolo test per vedere che la API funzioni correttamente o meglio funziona come tu te la aspetti.Se noi capiamo la API sarà più facile per noi scrivere successivamente il test.

12

Page 13: Test Driven Development for iOS

● Another test

Quando lavori e vedi un altro test da scrivere oppure ti viene in mente un test da scrivere, semplicemente aggiungilo alla lista dei test e continua con il test che stavi scrivendo.

● Regression testQuale è la prima cosa che fai quando trovi un bug? Scrivi il più semplice e corto test possibile affinchè il bug sia riprodotto. Vedi che il test fallisca. Cerca di far passare il test, di andare in barra verde.

13

Page 14: Test Driven Development for iOS

14

Page 15: Test Driven Development for iOS

TDD per una app iOS

FIZZ BUZZ :) Solo alcune regole per il gioco Fizz Buzz:

● Dicendo un numero devi dire lo stesso numero● Se il numero è multiplo di 3 devi dire “Fizz”● Se il numero è multiplo di 5 devi dire “Buzz”● Se il numero è multiplo di 3 e di 5 devi dire “Fizz Buzz”

Barra rossa:

Barra verde:

15

Page 16: Test Driven Development for iOS

Creazione oggetto Fizz Buzz

#import <UIKit/UIKit.h>#import <XCTest/XCTest.h>

#import "FizzBuzz.h"

@interface FizzBuzzTests : XCTestCase

@end

@implementation FizzBuzzTests

- (void)testFizzBuzz {FizzBuzz * fizzBuzz = [[FizzBuzz alloc]init];XCTAssertNotNil(fizzBuzz);

}

@end

16

Page 17: Test Driven Development for iOS

Creazione oggetto Fizz BuzzBuild failed: non conosce l’oggetto FizzBuzz perchè non esiste :) quindi: Barra RossaObiettivo andare in barra verde: creazione dell’oggettoCreo la classe:

File:FizzBuzz.h#import <UIKit/UIKit.h>

@interface FizzBuzz : UITextField

@end

File:FizzBuzz.m#import "FizzBuzz.h"

@implementation FizzBuzz

@end

Ora sono in barra verde.Ho dimostrato che sono in grado di creare l’oggetto FizzBuzz

17

Page 18: Test Driven Development for iOS

Processo iterativo: Ora che sono in barra verde mi scrivo il test successivo.

- (void)testFizzBuzzSay {

FizzBuzz * fizzBuzz = [[FizzBuzz alloc]init];

XCTAssertEqual(7, [fizzBuzz say:7]);

}

Ecco non compila: vado a scrive il metodo say:

@interface FizzBuzz : UITextField

-(CGFloat) say:(CGFloat) number;

@end

18

Page 19: Test Driven Development for iOS

Ora compila, eseguo i test e vedo barra rossa: test fallisce. Perchè non c’è l’implementazione.Devo andare in barra verde il prima possibile.Quindi mi scrivo l’implementazione nel file .m:#import "FizzBuzz.h"

@implementation FizzBuzz

-(CGFloat) say:(CGFloat) number;

{

return 7;

}

@end

Il test passa: sono in barra verde. Faccio un commit. Da osservare che l’implementazione è fake.Voglio aggiungere un test con un ulteriore numero ma prima vedo della duplicazione e la voglio togliere. Quindi il test da

19

Page 20: Test Driven Development for iOS

#import <UIKit/UIKit.h>

#import <XCTest/XCTest.h>

#import "FizzBuzz.h"

@interface FizzBuzzTests : XCTestCase

@end

@implementation FizzBuzzTests

- (void)testFizzBuzz {

FizzBuzz * fizzBuzz = [[FizzBuzz alloc]init];

XCTAssertNotNil(fizzBuzz);

}

- (void)testFizzBuzzSay {

FizzBuzz * fizzBuzz = [[FizzBuzz alloc]init];

XCTAssertEqual(7, [fizzBuzz say:7]);

}

@end

diventa

20

Page 21: Test Driven Development for iOS

#import <UIKit/UIKit.h>

#import <XCTest/XCTest.h>

#import "FizzBuzz.h"

@interface FizzBuzzTests : XCTestCase

@end

@implementation FizzBuzzTests

{

FizzBuzz * fizzBuzz;

}

- (void)setUp {

// Put setup code here. This method is called before the invocation of each test

method in the class.

[super setUp];

fizzBuzz = [[FizzBuzz alloc]init];

}

- (void)testFizzBuzz {

XCTAssertNotNil(fizzBuzz);

}

....21

Refactoring

Page 22: Test Driven Development for iOS

- (void)testFizzBuzzSay {

XCTAssertEqual(7, [fizzBuzz say:7]);

}

@end

Ora non ho più codice duplicato. Sono in barra verde: nuovo commitAggiungo il nuovo test: voglio che lanciando ad esempio 38 mi ritorni 38.- (void)testFizzBuzzSayThirtyEight {

XCTAssertEqualObjects(@"38", [fizzBuzz say:38]);

}

Fallisce il test quindi barra rossa. Scrivo l’implementazione necessaria: la più ovvia per far passare il test.#import "FizzBuzz.h"

@implementation FizzBuzz

-(CGFloat) say:(CGFloat) number;

{

return number;

}

@end

Ora rieseguo i test e i test passano. Commit.22

Page 23: Test Driven Development for iOS

Faccio un refactoring: rename del nome di un metodo di test: barra verde e poi commit.Da- (void)testFizzBuzzSay {

XCTAssertEqual(7, [fizzBuzz say:7]);

}

A- (void)testFizzBuzzSaySeven {

XCTAssertEqual(7, [fizzBuzz say:7]);

}

Mi scrivo un nuovo test: voglio che dicendo 3 risponda Fizz.- (void)testFizzBuzzSayThree {

XCTAssertEqual("Fizz", [fizzBuzz say:3]);

}

La build fallisce: c’è un problema sul tipo di ritorno. Voglio una stringa ma ritorna un numero.La modifica andrebbe a impattare anche gli altri metodi già esistenti: quindi mi riscrivo il metodo di test con play.

23

Refactoring

Page 24: Test Driven Development for iOS

- (void)testFizzBuzzSayThree {

XCTAssertEqual(@"Fizz", [fizzBuzz play:3]);

}

Non compila: barra rossa. Mi vado a scrivere l’implementazione.#import <UIKit/UIKit.h>

@interface FizzBuzz : UITextField

-(CGFloat) say:(CGFloat) number;

-(NSString *) play:(CGFloat) number;

@end

ora compila ma il test fallisce e quindi mi vado a scrivere l’implementazione nel file m.24

Page 25: Test Driven Development for iOS

#import "FizzBuzz.h"

@implementation FizzBuzz

-(CGFloat) say:(CGFloat) number;

{

return number;

}

-(NSString *) play:(CGFloat) number;

{

return @"Fizz";

}

@end

Ora sono in barra verde e faccio commit.So che il metodo say non mi serve più e faccio refactoring.Cambio il primo test che avevo scritto con il play- (void)testFizzBuzzSaySeven {

XCTAssertEqualObjects(@"7", [fizzBuzz play:7]);

}

Il test fallisce. Scrivo l’implementazione.25

Page 26: Test Driven Development for iOS

-(NSString *) play:(CGFloat) number;

{

if (number == 3)

return @"Fizz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

da notare che l’obiettivo è sempre far passare il test e non scrivere complicati algoritmi che comprendano tutti i test case possibili: il processo è incrementale. I test passano quindi barra verde e quindi nuovo commit.Nel metodo ho ancora un metodo say che voglio andare ad eliminare.Quindi nel test:

- (void)testFizzBuzzSayThirtyEight {

XCTAssertEqualObjects(@"38", [fizzBuzz play:38]);

}

Ora posso andare ad eliminare il metodo say nella classe di produzione. 26

Refactoring

Page 27: Test Driven Development for iOS

Quindi riassumendo ho la classe di test

#import <UIKit/UIKit.h>

#import <XCTest/XCTest.h>

#import "FizzBuzz.h"

@interface FizzBuzzTests : XCTestCase

@end

@implementation FizzBuzzTests

{

FizzBuzz * fizzBuzz;

}

- (void)setUp {

[super setUp];

fizzBuzz = [[FizzBuzz alloc]init];

}

- (void)testFizzBuzz {

XCTAssertNotNil(fizzBuzz);

}

- (void)testFizzBuzzSaySeven {

XCTAssertEqualObjects(@"7", [fizzBuzz play:7]);

}

- (void)testFizzBuzzSayThirtyEight {

XCTAssertEqualObjects(@"38", [fizzBuzz play:38]);

}

- (void)testFizzBuzzSayThree {

XCTAssertEqualObjects(@"Fizz", [fizzBuzz play:3]);

}

@end27

Page 28: Test Driven Development for iOS

e i file FizzBuzz .h e .m

#import <UIKit/UIKit.h>

@interface FizzBuzz : UITextField

-(NSString *) play:(CGFloat) number;

@end

#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

if (number == 3)

return @"Fizz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

@end

28

Page 29: Test Driven Development for iOS

Da osservare la riduzione nel numero di metodi ed è stata tolta la duplicazione.

Mi scrivo il nuovo test- (void)testFizzBuzzSayFive {

XCTAssertEqualObjects(@"Buzz", [fizzBuzz play:5]);

}

e vedo che il test fallisce e vado ad implementarlo.#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

if (number == 3)

return @"Fizz";

if (number == 5)

return @"Buzz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

@end 29

Page 30: Test Driven Development for iOS

Eseguo i test e sono in barra verde, quindi commit.Mi scrivo il nuovo test: dico 6 e mi deve rispondere “Fizz”- (void)testFizzBuzzSaySix {

XCTAssertEqualObjects(@"Fizz", [fizzBuzz play:6]);

}

Quindi test rosso: non ho ancora fatto l’implementazione. Faccio l’implementazione:#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

CGFloat moduloResult = (float)((int)number % (int)3);

if (moduloResult == 0)

return @"Fizz";

if (number == 5)

return @"Buzz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

@end

Ora siamo in barra verde: ho scritto l’implementazione.30

Page 31: Test Driven Development for iOS

Ora per qualsiasi multiplo di tre risponde Fizz.

Ora scrivo un test per 10: è multiplo di 5 e voglio che risponda Buzz.

- (void)testFizzBuzzSayTen {

XCTAssertEqualObjects(@"Buzz", [fizzBuzz play:10]);

}

31

Page 32: Test Driven Development for iOS

Scrivo l’implementazione:#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

CGFloat moduloResult = (float)((int)number % (int)3);

CGFloat moduloResultDividend5 = (float)((int)number % (int)5);

if (moduloResult == 0)

return @"Fizz";

if (moduloResultDividend5 == 0)

return @"Buzz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

@end

32

Page 33: Test Driven Development for iOS

Quindi ora sono barra verde e faccio del refactoring.Rename di una variabile e tolgo codice duplicato.Codice:#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

CGFloat moduloResultDividend3 = (float)((int)number % (int)3);

CGFloat moduloResultDividend5 = (float)((int)number % (int)5);

if (moduloResultDividend3 == 0)

return @"Fizz";

if (moduloResultDividend5 == 0)

return @"Buzz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

@end

Rieseguo i test, sono verdi, quindi commit.33

Refactoring

Page 34: Test Driven Development for iOS

Ora mi scrivo un metodo che mi fa la divisione e mi da il resto, per poi successivamente fare del refactoring: non cambio il comportamento, per togliere la duplicazione.

-(CGFloat) moduloOfNumber:(CGFloat) number

dividend:(int) dividend

{

return (float)((int)number % (int)dividend);

}

Inizio con il refactoring vero e proprio

34

Page 35: Test Driven Development for iOS

#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

CGFloat moduloResultDividend3 = [self moduloOfNumber:number dividend:3];

CGFloat moduloResultDividend5 = [self moduloOfNumber:number dividend:5];

if (moduloResultDividend3 == 0)

return @"Fizz";

if (moduloResultDividend5 == 0)

return @"Buzz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

-(CGFloat) moduloOfNumber:(CGFloat) number

dividend:(int) dividend

{

return (float)((int)number % (int)dividend);

}

@end

Rieseguo i test, sono verdi, quindi commit. 35

Page 36: Test Driven Development for iOS

Continuo con il refactoring:#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

if ([self moduloOfNumber:number dividend:3] == 0)

return @"Fizz";

if ([self moduloOfNumber:number dividend:5] == 0)

return @"Buzz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

-(CGFloat) moduloOfNumber:(CGFloat) number

dividend:(int) dividend

{

return (float)((int)number % (int)dividend);

}

@end

Rieseguo i test, sono verdi, quindi commit. 36

Refactoring

Page 37: Test Driven Development for iOS

Per completare il gioco mi manca l’ultima implementazione:se un numero è sia multiplo di 3, sia multiplo di 5, deve dire “Fizz Buzz”.Mi scrivo un test, come sempre :)

Un numero multiplo di 3 e 5 ad esempio è15 30 45 60 90Mi scrivo ad esempio un test per 30 che mi deve dare “Fizz Buzz”.- (void)testFizzBuzzSayThirty {

XCTAssertEqualObjects(@"Fizz Buzz", [fizzBuzz play:30]);

}

Ora mi scrivo un if che rappresenta la modifica minima per far passare il test.if (([self moduloOfNumber:number dividend:3] == 0) &&

([self moduloOfNumber:number dividend:5] == 0))

return @"Fizz Buzz";

I test passano, faccio un commit.ora posso fare il refactoring, perchè il codice è sotto test.

37

Page 38: Test Driven Development for iOS

Quindi devo fare refactoring di questa classe:#import "FizzBuzz.h"

@implementation FizzBuzz

-(NSString *) play:(CGFloat) number;

{

if (([self moduloOfNumber:number dividend:3] == 0) &&

([self moduloOfNumber:number dividend:5] == 0))

return @"Fizz Buzz";

if ([self moduloOfNumber:number dividend:3] == 0)

return @"Fizz";

if ([self moduloOfNumber:number dividend:5] == 0)

return @"Buzz";

else

return [NSString stringWithFormat: @"%.0f", number];

}

38

Refactoring

Page 39: Test Driven Development for iOS

-(CGFloat) moduloOfNumber:(CGFloat) number

dividend:(int) dividend

{

return (float)((int)number % (int)dividend);

}

@end

Faccio refactoring... :)))

39

Refactoring

Page 40: Test Driven Development for iOS

Utilizza i RED BAR PATTERNS:● Evident DataQuando nel test, expected e l’actual sono evidenti, quando la loro relazione nel test è evidente. ● Quando scrivi il test scrivilo ricordandoti del “One step test”

Quale test devi prendere per primo nella tua lista dei test? Devi prendere il test che ti insegnerà qualcosa e sei sicuro che sei confidente nella sua implementazione.

● Rivedi la tua lista dei test periodicamente: la priorità della lista dei test può cambiare di continuo

40

Page 41: Test Driven Development for iOS

Utilizza i GREEN BAR PATTERNS:

● Fake it: quale è la tua implementazione quando hai un test che si rompe? Ritorna una costante. Una volta che tu hai un test in esecuzione, gradualmente trasforma la costante in una espressione che utilizza variabili. Ci sono due effetti per fare la fake implementation. Avere la barra verde ti consente di sentirti in modo diverso quando fai refactoring. Inoltre partire con un esempio concreto ti consente una generalizzazione migliore, risolvere il problema in modo migliore. Inoltre sei focalizzato su un solo test, quel test. E in più sai che appena prima sei in barra verde.

● Obvious implementation: come puoi implementarla nel modo più semplice? Semplicemente inizia a scrivere l’implementazione. Ricorda che l’implementazione: 1) deve funzionare 2) il codice deve essere pulito (clean code).

● One to many: come implementare una operazione che funziona con una collezione di oggetti? Prima scrivi il test per un oggetto e poi il test per la collezione.

41

Page 42: Test Driven Development for iOS

Fixture

Se noi creiamo oggetti comuni per diversi test possiamo metterli nel setup ed inizializzare quelle variabili, come si vuole togliere la duplicazione nel codice di produzione allo stesso modo vogliamo togliere la duplicazione nel codice dei test.Ricorda che il copia ed incolla porta alla duplicazione quindi se fai copia e incolla nei test ricorda che stai introducendo duplicazione.Inoltre i test con il setUp scritto correttamente sono molto più leggibili, le classi di test sono molto più leggibili in ogni sua parte.External fixture.Come rilasciamo le risorse create nella fixture? Grazie al tearDown vengono rilasciate le risorse.Ogni test deve lasciare “il mondo esattamente come lo ha trovato”. Alla fine dell’esecuzione dei test non devono esserci modifiche fatte prima della esecuzione dei test stessi.

42

Page 43: Test Driven Development for iOS

Test methodCome si inizia a scrivere un test: con il nome del test che inizia con test.I metodi dei test dovrebbero essere facili da leggere ed anche corti.

Exception testDevi testare le eccezioni che ti aspetti. Il test fallisce se l’eccezione non è lanciata. Non sempre i test per lanciare le eccezioni sono semplici da implementare.

All testsCome eseguire tutti i test insieme? Scrivi una suite di tutti i test, una suite per ciascun package oppure una suite per gruppi di package.

43

Page 44: Test Driven Development for iOS

REFACTORING

I pattern di refactoring descrivono come cambiare il design del sistema per mezzo di small step.Il refactoring non cambia la semantica del sistema sotto nessuna circostanza.Ad esempio nel TDD se noi cambiamo le costanti con le variabili è un refactoring perchè non cambia il set di test che passano.Reconcile difference:come unificare due parti di codice simili? Semplicemente portali vicine. Unificale semplicemente quando sono identiche. Se io estraggo un metodo e lo faccio in modo meccanico, e molto difficile che io rompi il comportamento. Ma se un refactoring mi spinge ad esaminare il controllo del flusso e il valore dei dati allora si deve stare attenti.Il refactoring lo puoi vedere in diversi casi.Se due strutture a loop sono simile, per mezzo di una riscrittura identica delle due strutture, tu puoi fare il merge delle due strutture.

44

Page 45: Test Driven Development for iOS

REFACTORING (2)

Se due branch di un condizionale sono simili, creandole identiche tu puoi eliminare il condizionale.Se due metodi sono simili, per mezzo, se li rendi identici, puoi eliminare un metodo.Se due classi sono identiche, modificandole e creandole identiche tu puoi successivamente eliminare una classe.Un esempio se vuoi rimuovere una sottoclasse, un approccio è che la sottoclasse non deve contenere niente, successivamente la superclasse può sostituire la sottoclasse senza cambiare comportamento al sistema.Per svuotare la sottoclasse, la superclasse deve avere il metodo identico alla sottoclasse. Uno a uno svuota tutti i metodi della sotoclasse poi quando la sottoclasse non ha più metodi, allora sostituisci la referenza della sottoclasse con quella della superclasse.

45

Page 46: Test Driven Development for iOS

Isolate Change

Vuoi cambiare una parte o più parti di un metodo di un oggetto.Prima isola la parte di codice che vuoi cambiare.Bilancia il costo di aggiungere un metodo con l’eventuale valore di avere un concetto aggiuntivo esplicito nel codice. Alcuni modi per isolare i cambiamenti sono l’ Extract Method, Extract Object, Method Object, Migrate Data.

Ma anche inline method (semplifica il flusso di controllo quando diventa complicato).Il Move Method, l’Extract Interface e il Method Parameter to Constructor Parameter.

46

Page 47: Test Driven Development for iOS

Considerazione sul feedback che ti da il codice

Se stai scrivendo un test che si rompe e non sai cosa fare è una evidenza che tu non hai conoscenze a sufficienza sul design del programma.Quando rompi un test, il tuo primo obbiettivo è farlo passare il più rapidamente in barra verde.

47

Page 48: Test Driven Development for iOS

TDD for UI (iOS) (ios-snapshot-test-case)Per l’installazione: https://github.com/facebook/ios-snapshot-test-case La mia guida: https://goo.gl/IzaUWn

Mi scrivo il test degli Snapshot#import <Foundation/Foundation.h>#import <FBSnapshotTestCase/FBSnapshotTestCase.h>#import "MyViewController.h"

@interface SnapshotTestCase : FBSnapshotTestCase@end

@implementation SnapshotTestCase- (void) test_fizz_buzz{

//self.recordMode = YES;MyViewController* viewController = [[self getStoryboard] instantiateViewControllerWithIdentifier:@"MyViewController"];

[viewController viewDidLoad];

FBSnapshotVerifyView(viewController.view, nil);}

-(UIStoryboard*)getStoryboard{

return [UIStoryboard storyboardWithName:@"Main" bundle:nil];}@end 48

Page 49: Test Driven Development for iOS

Barra rossa: non ha immagini registrateAbilito self.recordMode = YES;E vedo che è l’immagine registrata non è quella voluta (una immagine con un text field un bottone e una label)

Vado a fare l’implementazione:● Grazie ad Xcode aggiungo nel Main.storyboard:

UITextField come input per il numeroUILabel che visualizza la stringa del giocoUIButton un bottone che leggerà da UITextField elabora l’input e visualizza l’output per grazie alla UILabel.● Setto nel Main.storyboard in alto a destra la custom class MyViewController

che è il mio MyViewController.

49

Page 50: Test Driven Development for iOS

Rieseguo il test registrando l’immagine e ho l’immagine voluta. Barra Verde.

50

Page 51: Test Driven Development for iOS

Ora disabilito il registration mode e commit.Ho registrato la UI, ora qualsiasi modifica grafica sui 3 componenti il test fallirà e mi avvertirà.

- (void) test_fizz_buzz{

//self.recordMode = YES;MyViewController* viewController = [[self getStoryboard] instantiateViewControllerWithIdentifier:@"MyViewController"];

[viewController viewDidLoad];

FBSnapshotVerifyView(viewController.view, nil);}

51

Page 52: Test Driven Development for iOS

Ora aggancio la logica nel ViewController:#import "MyViewController.h"#import "FizzBuzz.h"

@interface MyViewController ()@property (weak, nonatomic) IBOutlet UITextField *field;@property (weak, nonatomic) IBOutlet UILabel *result;@end

@implementation MyViewController- (void)viewDidLoad {

[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.

}- (IBAction)play:(id)sender {

NSLog(@"play Button _field.text[%@]",_field.text);FizzBuzz * fizzBuzz = [[FizzBuzz alloc]init];_result.text = [fizzBuzz play:[_field.text floatValue]];

}- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.

}@end

e l’app è implementata!!!

52

Page 53: Test Driven Development for iOS

SITOGRAFIA / BIBLIOGRAFIA:

● TDD by Example Kent Beck

● http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd

● Test-Driven iOS Development by Graham Lee (Author)

● http://c2.com/cgi/wiki?XpSimplicityRules

● http://martinfowler.com/bliki/BeckDesignRules.html

53

Page 54: Test Driven Development for iOS

Test Driven Development for iOS

Alessandro Ceseno

contact me, my website is: www.alexceseno.itAdd me on Linkedin:

http://www.linkedin.com/in/alessandrocesenoTwitter:

https://twitter.com/AlexCeseno