CocoaHeads Rennes #10
                              Quentin Arnault
     10 mai 2012




        Mock objects, théorie et
             application
Ce que vous allez apprendre


➡   ce que sont les mock objects,

➡   ce qu’ils vous apporteront,

➡   les utiliser grâce à OCMock.
Contexte et
regard sur la POO

                                                       Retour
                                                    d’expérience
                        Photo
                    d’identité et
                     photo de
                       famille
                                    Montre moi du
                                       code !
Contexte et regard sur la POO
plus facile
tests automatisés


                    Contexte ?
    plus efficace                                       outils

                      meilleure maintenabilité
3 niveaux de tests automatisés


       • unitaire,
       • intégration,
       • de bout en bout.                    unitaire
                                                                       intégration

                                                                    qualité du feeback           bout en bout
                                                                     qualité externe     qualité interne
Steve Freeman, Nat Pryce. Growing Object-oriented software, Guided by tests, p11
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
C’est à dire
                           Webservice Client


    Responsabilités                      Collaborateurs

➡   construire les requêtes HTTP    ➡    client HTTP

➡   traiter les erreurs HTTP/svc    ➡    parser

➡   informer du résultat            ➡    observateur
HTTP Client
Webservice Client
                    Webservice Client
   Delegate
                                          Parser
...                       ...     ...    ...
      ...


                                                  HTTP
                  ...
                                                   Cl.


                         observ.   W. Cl.



                                                  parser         ...         ...
                   ...



                          ...
...         ...                                                  ...


                                                                       ...
                          ...
...                       ...     ...    ...
      ...


                                                  HTTP
                                                   test
                  ...
                                                  object
                                                    Cl.


                           test
                         observ.   W. Cl.
                          object


                                                   test
                                                  parser         ...         ...
                   ...                            object



                          ...
...         ...                                                  ...


                                                                       ...
                          ...
Photo d’identité et photo de
          famille
objets factices (dummy object)


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

➡   mais pas de comportements
objets bouchon (stub object)


➡   vient remplacer un objet de production

➡   retourne des valeurs

➡   comportement souvent partiel
objets allégés (fake object)


➡   vient remplacer un objet de production

➡   contient une implémentation fonctionnelle mais non adaptée à
    la production
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
Montre moi du code !
Une installation simple
1



2



3
Premiers pas
HTTPClient *httpClient = [[HTTPClient alloc] init];

WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:httpClient];
Premiers pas
id mockClient = [OCMockObject mockForClass:[HTTPClient class]];

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


[[mockClient expect] prepare];              Enregistrement du contrat


[client fetchUsers];                        Enregistrement des messages

[mockClient verify];
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){}]];
verify
                                                [mock verify];

Test Suite 'Tests' started.
Starting TestIsLife/test_missing_call
2012-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
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];
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]];
id mockClient = [OCMockObject mockForClass:[HTTPClient class]];




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

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


    [[mockClient expect] prepare];
    [[[mockClient stub] andReturn:@"tournament"] getBaseUrl];
    [[[mockClient stub] andReturn:@"HTTPS"] getProtocol];
    [[[mockClient stub] setTimeout:10];
id mockClient = [OCMockObject niceMockForClass:[HTTPClient class]];



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

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


    [[mock expect] prepare]




    [[mock reject] badMethod]
id mockClient = [OCMockObject partialMockForObject:httpClient];




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

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




    [[[mock expect] andForwardToRealObject] prepare]
id mockObserver = [OCMockObject observerMock];




➡   permet d’observer des notifications


    [notificationCenter addMockObserver:mockObserver
                                   name:notificationName
                                 object:nil];
    [[mockObserver expect] notificationWithName:notificationName
                                         object:[OCMArg any]]
Retour d’expérience
- (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];
}
@property (nonatomic, readonly) id mockClient;

@synthesize mockClient = mockClient_;

- (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];
}


- (id)mockClient {
    if (!mockClient_)
        mockClient_ = [ OCMockObject mockForClass:[HTTPClient class]];
        [[mockClient_ stub] setTimeout:[OCMArg any]];
    }
    return mockClient_;
- (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];
}
- (void)test_should_initilize_http_client {
    // arrange
    [[self.mockClient expect] prepare];
    WebServiceClient *client = [[WebServiceClient alloc]
initWithHTTPClient:self.mockClient];

    // act
    [client fetchUsers];
}



- (void)setUp {
    [super setUp];

    mockClient_ = nil;
}

- (void)tearDown {
    [super tearDown];

    [self.mockClient verify];
}
Pourquoi les utiliser ?


➡   minimisation des interactions

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

➡   tests plus rapide
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
Références
➡   http://www.cocoaheads.fr

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

➡   http:// www.mockobjects.com

➡   http://ocmock.org

➡   http://jamesmead.org/talks/2007-07-09-introduction-to-mock-
    objects-in-ruby-at-lrug/
Écrivez les tests que vous
      voudriez lire.
julien@cocoaheads.fr
  CocoaHeads #10                        thomas.dupont@cocoaheads.fr




Mock objects, théorie et application

   Mail   : quentin.arnault@gmail.com

CocoaHeads Rennes #10 : Mock Objects

  • 1.
    CocoaHeads Rennes #10 Quentin Arnault 10 mai 2012 Mock objects, théorie et application
  • 2.
    Ce que vousallez apprendre ➡ ce que sont les mock objects, ➡ ce qu’ils vous apporteront, ➡ les utiliser grâce à OCMock.
  • 3.
    Contexte et regard surla POO Retour d’expérience Photo d’identité et photo de famille Montre moi du code !
  • 4.
  • 5.
    plus facile tests automatisés Contexte ? plus efficace outils meilleure maintenabilité
  • 6.
    3 niveaux detests automatisés • unitaire, • intégration, • de bout en bout. unitaire intégration qualité du feeback bout en bout qualité externe qualité interne Steve Freeman, Nat Pryce. Growing Object-oriented software, Guided by tests, p11
  • 7.
    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
  • 8.
    C’est à dire Webservice Client Responsabilités Collaborateurs ➡ construire les requêtes HTTP ➡ client HTTP ➡ traiter les erreurs HTTP/svc ➡ parser ➡ informer du résultat ➡ observateur
  • 9.
    HTTP Client Webservice Client Webservice Client Delegate Parser
  • 11.
    ... ... ... ... ... HTTP ... Cl. observ. W. Cl. parser ... ... ... ... ... ... ... ... ...
  • 12.
    ... ... ... ... ... HTTP test ... object Cl. test observ. W. Cl. object test parser ... ... ... object ... ... ... ... ... ...
  • 13.
    Photo d’identité etphoto de famille
  • 14.
    objets factices (dummyobject) ➡ vient remplacer «bêtement» l’objet de production ➡ mais pas de comportements
  • 15.
    objets bouchon (stubobject) ➡ vient remplacer un objet de production ➡ retourne des valeurs ➡ comportement souvent partiel
  • 16.
    objets allégés (fakeobject) ➡ vient remplacer un objet de production ➡ contient une implémentation fonctionnelle mais non adaptée à la production
  • 17.
    mocks objects (mockobject) ➡ 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
  • 18.
  • 19.
  • 20.
    Premiers pas HTTPClient *httpClient= [[HTTPClient alloc] init]; WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:httpClient];
  • 21.
    Premiers pas id mockClient= [OCMockObject mockForClass:[HTTPClient class]]; WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:mockClient]; [[mockClient expect] prepare]; Enregistrement du contrat [client fetchUsers]; Enregistrement des messages [mockClient verify];
  • 22.
    expect [[mock expect] aMethod]; [[mockexpect] 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){}]];
  • 23.
    verify [mock verify]; Test Suite 'Tests' started. Starting TestIsLife/test_missing_call 2012-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
  • 24.
    stub [[mock stub] aMethod]; [[[mockstub] 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];
  • 25.
    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]];
  • 26.
    id mockClient =[OCMockObject mockForClass:[HTTPClient class]]; ➡ permet de préciser les appels de méthodes que l’on attend ➡ ne supporte aucun appel en dehors de ceux définis [[mockClient expect] prepare]; [[[mockClient stub] andReturn:@"tournament"] getBaseUrl]; [[[mockClient stub] andReturn:@"HTTPS"] getProtocol]; [[[mockClient stub] setTimeout:10];
  • 27.
    id mockClient =[OCMockObject niceMockForClass:[HTTPClient class]]; ➡ permet de préciser les appels de méthodes que l’on attend ➡ ignore tous les appels de méthodes non prévus [[mock expect] prepare] [[mock reject] badMethod]
  • 28.
    id mockClient =[OCMockObject partialMockForObject:httpClient]; ➡ permet de préciser les appels de méthodes que l’on attend ➡ tout en conservant le comportement de l’objet remplacé [[[mock expect] andForwardToRealObject] prepare]
  • 29.
    id mockObserver =[OCMockObject observerMock]; ➡ permet d’observer des notifications [notificationCenter addMockObserver:mockObserver name:notificationName object:nil]; [[mockObserver expect] notificationWithName:notificationName object:[OCMArg any]]
  • 30.
  • 31.
    - (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]; }
  • 32.
    @property (nonatomic, readonly)id mockClient; @synthesize mockClient = mockClient_; - (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]; } - (id)mockClient { if (!mockClient_) mockClient_ = [ OCMockObject mockForClass:[HTTPClient class]]; [[mockClient_ stub] setTimeout:[OCMArg any]]; } return mockClient_;
  • 33.
    - (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]; }
  • 34.
    - (void)test_should_initilize_http_client { // arrange [[self.mockClient expect] prepare]; WebServiceClient *client = [[WebServiceClient alloc] initWithHTTPClient:self.mockClient]; // act [client fetchUsers]; } - (void)setUp { [super setUp]; mockClient_ = nil; } - (void)tearDown { [super tearDown]; [self.mockClient verify]; }
  • 35.
    Pourquoi les utiliser? ➡ minimisation des interactions ➡ minimisation de l’exposition de l’état de l’objet testé ➡ tests plus rapide
  • 36.
    Ce qu’il m’ontapporté ? ➡ 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
  • 37.
    Références ➡ http://www.cocoaheads.fr ➡ Steve Freeman, Nat Pryce. Growing Object-oriented software, Guided by tests ➡ http:// www.mockobjects.com ➡ http://ocmock.org ➡ http://jamesmead.org/talks/2007-07-09-introduction-to-mock- objects-in-ruby-at-lrug/
  • 38.
    Écrivez les testsque vous voudriez lire.
  • 39.
    julien@cocoaheads.fr CocoaHeads#10 thomas.dupont@cocoaheads.fr Mock objects, théorie et application Mail : quentin.arnault@gmail.com