test driven development for ios
TRANSCRIPT
![Page 1: Test Driven Development for iOS](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/1.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/2.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/3.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/4.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/5.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/6.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/7.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/8.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/9.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/10.jpg)
● 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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/11.jpg)
● 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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/12.jpg)
● 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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/13.jpg)
● 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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/14.jpg)
14
![Page 15: Test Driven Development for iOS](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/15.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/16.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/17.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/18.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/19.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/20.jpg)
#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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/21.jpg)
#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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/22.jpg)
- (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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/23.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/24.jpg)
- (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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/25.jpg)
#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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/26.jpg)
-(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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/27.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/28.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/29.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/30.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/31.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/32.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/33.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/34.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/35.jpg)
#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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/36.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/37.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/38.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/39.jpg)
-(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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/40.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/41.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/42.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/43.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/44.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/45.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/46.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/47.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/48.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/49.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/50.jpg)
Rieseguo il test registrando l’immagine e ho l’immagine voluta. Barra Verde.
50
![Page 51: Test Driven Development for iOS](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/51.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/52.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/53.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022419/589f4c9f1a28abec418b5323/html5/thumbnails/54.jpg)
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