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

CocoaHeads Rennes #10 : Mock Objects

Jun 26, 2015

Download

Technology

CocoaHeadsRNS

Slides de la session des CocoaHeads Rennaise du 10 mai 2012. Session présentée par Quentin Arnault.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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.