39
Mock objects, théorie et application Quentin Arnault CocoaHeads Rennes #10 10 mai 2012

CocoaHeads Rennes #10 : Mock Objects

Embed Size (px)

DESCRIPTION

Slides de la session des CocoaHeads Rennaise du 10 mai 2012. Session présentée par Quentin Arnault.

Citation preview

Page 1: CocoaHeads Rennes #10 : Mock Objects

Mock objects, théorie et application

Quentin ArnaultCocoaHeads Rennes #1010 mai 2012

Page 2: CocoaHeads Rennes #10 : Mock Objects

Ce que vous allez apprendre

➡ ce que sont les mock objects,

➡ ce qu’ils vous apporteront,

➡ les utiliser grâce à OCMock.

Page 3: CocoaHeads Rennes #10 : Mock Objects

Contexte et regard sur la POO

Photo d’identité et

photo de famille

Montre moi du code !

Retour d’expérience

Page 4: CocoaHeads Rennes #10 : Mock Objects

Contexte et regard sur la POO

Page 5: CocoaHeads Rennes #10 : Mock Objects

Contexte ?

tests automatisés

outils

plus facile

plus efficace

meilleure maintenabilité

Page 6: CocoaHeads Rennes #10 : Mock Objects

3 niveaux de tests automatisés

• unitaire,

• intégration,

• de bout en bout. unitaire

intégration

bout en boutqualité du feeback

Steve Freeman, Nat Pryce. Growing Object-oriented software, Guided by tests, p11

qualité externe qualité interne

Page 7: CocoaHeads Rennes #10 : Mock Objects

Back to basics (POO)

➡ Responsabilité : obligation d’accomplir une tâche ou de détenir une information

➡ Rôle : ensemble de responsabilités liées entre elles

➡ Objet : implémentation d’un ou plusieurs rôles

➡ Collaboration : interactions d’ objets ou de rôles

Page 8: CocoaHeads Rennes #10 : Mock Objects

C’est à dire

Responsabilités

➡ construire les requêtes HTTP

➡ traiter les erreurs HTTP/svc

➡ informer du résultat

Collaborateurs

➡ client HTTP

➡ parser

➡ observateur

Webservice Client

Page 9: CocoaHeads Rennes #10 : Mock Objects

Webservice ClientWebservice Client Delegate

HTTP Client

Parser

Page 10: CocoaHeads Rennes #10 : Mock Objects
Page 11: CocoaHeads Rennes #10 : Mock Objects

W. Cl.

HTTP Cl.

parser

observ.

...

...

...

...... ...

...

...

...

... ...

...

...

...

... ...

Page 12: CocoaHeads Rennes #10 : Mock Objects

W. Cl.

HTTP Cl.

parser

observ.

...

...

...

...... ...

...

...

...

... ...

...

...

... ...

test object

test object

test object

Page 13: CocoaHeads Rennes #10 : Mock Objects

Photo d’identité et photo de famille

Page 14: CocoaHeads Rennes #10 : Mock Objects

objets factices (dummy object)

➡ vient remplacer «bêtement» l’objet de production

➡ mais pas de comportements

Page 15: CocoaHeads Rennes #10 : Mock Objects

objets bouchon (stub object)

➡ vient remplacer un objet de production

➡ retourne des valeurs

➡ comportement souvent partiel

Page 16: CocoaHeads Rennes #10 : Mock Objects

objets allégés (fake object)

➡ vient remplacer un objet de production

➡ contient une implémentation fonctionnelle mais non adaptée à la production

Page 17: CocoaHeads Rennes #10 : Mock Objects

mocks objects (mock object)

➡ vient remplacer un objet de production

➡ peut retourner des valeurs (comme les objets bouchons)

➡ contrôle les messages reçus par rapport au contrat prévu

➡ Première phase : enregistrement du contrat

➡ Deuxième phase : enregistrement des messages pendant l’exécution du cas de test

Page 18: CocoaHeads Rennes #10 : Mock Objects

Montre moi du code !

Page 19: CocoaHeads Rennes #10 : Mock Objects

Une installation simple1

2

3

Page 20: CocoaHeads Rennes #10 : Mock Objects

Premiers pas

WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:httpClient];

HTTPClient *httpClient = [[HTTPClient alloc] init];

Page 21: CocoaHeads Rennes #10 : Mock Objects

WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:mockClient];

id mockClient = [OCMockObject mockForClass:[HTTPClient class]];

Premiers pas

[[mockClient expect] prepare];

[mockClient verify];

[client fetchUsers];

Enregistrement du contrat

Enregistrement des messages

Page 22: CocoaHeads Rennes #10 : Mock Objects

expect[[mock expect] aMethod];

[[mock expect] aMethodWithParameter:anObject];

[[mock expect] aMethodWithParameter:[OCMArg any]];

[[mock expect] aMethodWithParameter:[OCMArg anyPointer]];

[[mock expect] aMethodWithParameter:[OCMArg isNotEqual:aValue]];

[[mock expect] aMethodWithParameter:[OCMArg checkWithSelector:@selector() onObject:anObject]];

[[mock expect] aMethodWithParameter:[OCMArg checkWithBlock:^BOOL(id value){}]];

Page 23: CocoaHeads Rennes #10 : Mock Objects

verify[mock verify];

Test Suite 'Tests' started.Starting TestIsLife/test_missing_call2012-05-10 14:47:49.413 runTests[10490:13303] ! Name: NSInternalInconsistencyException! File: Unknown! Line: Unknown! Reason: OCMockObject[HTTPClient]: expected method was not invoked: prepare

#0 0x1b9a022 __exceptionPreprocess() (/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation)#1 0x1f76cd6 objc_exception_throw() (/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk/usr/lib/libobjc.A.dylib)#2 0x1b42a48 +[NSException raise:format:arguments:] (/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation)#3 0x1b429b9 +[NSException raise:format:] (/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/...Blah blah blah...

Page 24: CocoaHeads Rennes #10 : Mock Objects

stub

[[mock stub] aMethod];

[[[mock stub] andReturn:aValue] aMethod];

[[[mock stub] andThrow:anException] aMethod];

[[[mock stub] andPost:aNotification] aMethod];

[[[mock stub] andCall:@selector() onObject:anObject] aMethod];

[[[mock stub] andDo:^(NSInvocation *){}] aMethod];

Page 25: CocoaHeads Rennes #10 : Mock Objects

stub[[[mock stub] andReturn:aValue] aMethod];

[[[mock stub] andPost:aNotification] aMethod];

[[[[mock stub] andReturn:aValue] andPost:aNotification] aMethod];

[[[mock stub] andReturn:aValue] aMethodWithParameter:[OCMArg isNotNil]];

[[[mock stub] andThrow:anException] aMethodWithParameter:[OCMArg isNil]];

Page 26: CocoaHeads Rennes #10 : Mock Objects

➡ permet de préciser les appels de méthodes que l’on attend

➡ ne supporte aucun appel en dehors de ceux définis

id mockClient = [OCMockObject mockForClass:[HTTPClient class]];

[[mockClient expect] prepare];[[[mockClient stub] andReturn:@"tournament"] getBaseUrl];[[[mockClient stub] andReturn:@"HTTPS"] getProtocol];[[[mockClient stub] setTimeout:10];

Page 27: CocoaHeads Rennes #10 : Mock Objects

➡ permet de préciser les appels de méthodes que l’on attend

➡ ignore tous les appels de méthodes non prévus

id mockClient = [OCMockObject niceMockForClass:[HTTPClient class]];

[[mock expect] prepare]

[[mock reject] badMethod]

Page 28: CocoaHeads Rennes #10 : Mock Objects

➡ permet de préciser les appels de méthodes que l’on attend

➡ tout en conservant le comportement de l’objet remplacé

id mockClient = [OCMockObject partialMockForObject:httpClient];

[[[mock expect] andForwardToRealObject] prepare]

Page 29: CocoaHeads Rennes #10 : Mock Objects

➡ permet d’observer des notifications

id mockObserver = [OCMockObject observerMock];

[notificationCenter addMockObserver:mockObserver name:notificationName object:nil];[[mockObserver expect] notificationWithName:notificationName object:[OCMArg any]]

Page 30: CocoaHeads Rennes #10 : Mock Objects

Retour d’expérience

Page 31: CocoaHeads Rennes #10 : Mock Objects

- (void)test_should_initilize_http_client { // arrange id mockClient = [OCMockObject mockForClass:[HTTPClient class]]; [[mockClient stub] setTimeout:[OCMArg any]]; [[mockClient expect] prepare]; WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:mockClient];

// act [client fetchUsers];

// assert [mockClient verify];}

Page 32: CocoaHeads Rennes #10 : Mock Objects

- (void)test_should_initilize_http_client { // arrange [[self.mockClient expect] prepare]; WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:self.mockClient];

// act [client fetchUsers];

// verify [self.mockClient verify];}

@property (nonatomic, readonly) id mockClient;

@synthesize mockClient = mockClient_;

- (id)mockClient { if (!mockClient_) mockClient_ = [ OCMockObject mockForClass:[HTTPClient class]]; [[mockClient_ stub] setTimeout:[OCMArg any]]; } return mockClient_;}

Page 33: CocoaHeads Rennes #10 : Mock Objects

- (void)test_should_initilize_http_client { // arrange [[self.mockClient expect] prepare]; WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:self.mockClient];

// act [client fetchUsers];

// assert [self.mockClient verify];}

Page 34: CocoaHeads Rennes #10 : Mock Objects

- (void)setUp { [super setUp];

mockClient_ = nil;}

- (void)tearDown { [super tearDown];

[self.mockClient verify];}

- (void)test_should_initilize_http_client { // arrange [[self.mockClient expect] prepare]; WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:self.mockClient];

// act [client fetchUsers];}

Page 35: CocoaHeads Rennes #10 : Mock Objects

Pourquoi les utiliser ?

➡ minimisation des interactions

➡ minimisation de l’exposition de l’état de l’objet testé

➡ tests plus rapide

Page 36: CocoaHeads Rennes #10 : Mock Objects

Ce qu’il m’ont apporté ?

➡ différencier les tests d’état des tests de collaboration

➡ nouvel angle d’analyse d’un design : l’interaction VS la classification

➡ l’importance d’avoir des dépendances explicites

Page 38: CocoaHeads Rennes #10 : Mock Objects

Écrivez les tests que vous voudriez lire.