11
votes

Ocmock et des tests de blocage, exécutant

Voici la méthode sous test:

// OCUnit test method
- (void)test_loginWithUserPass_success_block_should_call_credentials_setAuthToken {

    void (^proxyBlock)(NSInvocation*) = ^(NSInvocation *invocation) {
        void(^successBlock)(NSString *authToken);
        [invocation getArgument:&successBlock atIndex:3]; // should be 3 because my block is the second param
        successBlock(@"myAuthToken");
    };

    [[[_loginController expect] andDo:proxyBlock] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
    [[_credentialStore expect] setAuthToken:@"myAuthToken"];
    [_docServiceSUT loginWithUser:@"mark" andPass:@"myPass"];
    [_loginController verify];
    [_credentialStore verify];
}

//method under test
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {

    NSDictionary *userPassD = @{@"user":userName,
                                @"pass":pass};

    void(^onSuccess)(NSString *) = ^(NSString *authToken){

        [SVProgressHUD dismiss];
        [_credentials setAuthToken:authToken];

        // Ask user to enter the 6 digit authenticator key
        [self askUserForAuthenticatorKey];
    };

    void(^onFailure)(NSString *) = ^(NSString *errorMessage) {

        [SVProgressHUD dismiss];
        [_alertSender sendAlertWithMessage:errorMessage andTitle:@"Login failed"];
    };

    [SVProgressHUD show];
    [_loginCntrl loginWithUserPass:userPassD withSuccess:onSuccess
      failure:onFailure];
}


0 commentaires

3 Réponses :


0
votes

Je pense que vous pouvez le faire avec un espion. Lire ce page wiki sur espions on dirait que vous pouvez capturer le bloc passé In et l'invoquez vous-même dans le test.


2 commentaires

Cela semble utile, mais signifierait passer d'Ocmock à Kiwi pour obtenir cette syntaxe.


Ah, j'ai dû négliger le fait qu'il utilisait Ocmock. +1 pour kiwi alors :)



13
votes

Si je vous suis à droite, cela peut faire ce que vous voulez:

@interface ExampleLC : NSObject
- (void)loginWithUserPass:userPassD withSuccess:(void (^)(NSString *authToken))successBlock failure:(void (^)(NSString *errorMessage))failureBlock;
@end
@implementation ExampleLC
- (void)loginWithUserPass:userPassD withSuccess:(void (^)(NSString *authToken))successBlock failure:(void (^)(NSString *errorMessage))failureBlock
{
}
@end
@interface Example : NSObject {
    @public
    ExampleLC *_loginCntrl;
}
- (void)saveToken:(NSString *)authToken;
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass;
@end
@implementation Example
- (void)saveToken:(NSString *)authToken
{
}
- (void)loginWithUser:(NSString *)userName andPass:(NSString *)pass {

    NSDictionary *userPassD = @{@"user":userName,
                                @"pass":pass};
    [_loginCntrl loginWithUserPass:userPassD withSuccess:^(NSString *authToken){
        // save authToken to credential store
        [self saveToken:authToken];
    } failure:^(NSString *errorMessage) {
        // alert user pass was wrong
    }];
}
@end


@interface loginTest : SenTestCase

@end

@implementation loginTest

- (void)testExample
{
    Example *exampleOrig = [[Example alloc] init];
    id loginCntrl = [OCMockObject mockForClass:[ExampleLC class]];
    [[[loginCntrl expect] andDo:^(NSInvocation *invocation) {
        void (^successBlock)(NSString *authToken) = [invocation getArgumentAtIndexAsObject:3];
        successBlock(@"Dummy");
    }] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];
    exampleOrig->_loginCntrl = loginCntrl;
    id example = [OCMockObject partialMockForObject:exampleOrig];
    [[example expect] saveToken:@"Dummy"];
    [example loginWithUser:@"ABC" andPass:@"DEF"];
    [loginCntrl verify];
    [example verify];
}
@end


6 commentaires

Le code réel ne compilera pas, a une erreur à la ligne: Void (^ SuccèsBlock) (Nstring * authtoken), syntaxe essentiellement incorrecte et j'ai vérifié la syntaxe de la même manière que je l'ai copiée, modifiant-le pour mes variables .


Il n'y a pas non plus de méthode getargumumpatindex, il existe cependant un getargument: AttinDex: méthode, qu'est-ce qui devrait aller dans le getargument? Mis à jour ci-dessus avec ce que j'ai qui compilait et courir, mais jetant une exception.


Vous devrez vous assurer que le NSInvocation + ocmaddition.h est inclus dans votre projet ( github.com/erikdoe/ocmock/blob/master/source/ocmock/... ) Cela vous donne accès à getArgumenteraDexasObject. Les deux premiers arguments sont les auto et cmd afin que vous souhaitiez le troisième si vous voulez votre bloc de réussite.


* et par "troisième" Idem Idem Idex 3 ... Self: CMD: LoginWithUserPass: WithSuccess: échecs J'avais l'exemple ci-dessus fonctionnant localement, alors c'est Devrait Travailler.


Prenez soin de la '[_logincntrl LoginwithUserPass: userPassd WithSuccess: ^ (Nstring * authToken) {// Sauvegarder authToken au magasin de références [SelfaveToken: AuthToken]; } Défaut: ^ (Nstring * errorormessage) {// ALERTER L'UTILISATEUR PASS était incorrect}]; ' Le «_logincntrl» est une référence forte et vous appeler à l'intérieur du bloc provoquera un cycle de conservation. En utilisant '__wak exemple. Cela ne le ferait pas important pour cet exemple, mais une bonne pratique non moins. ;)


Génial, ça marche pour moi mais je change à vide (^ Succèsblock) (Succès d'identification); [Invocation getargument: & SuccessBlock AttinDex: 4]; Succèsblock (ocmock_any);



7
votes

code que je travaille est fortement basé sur des blocs, donc je suis très familier avec votre question.

Juste pour reformuler le problème un peu: p>

- (void)mockSuccessBlockWithOneArgumentTwoBlocks:(id)firstArgument
                                    successBlock:(void (^)(NSString *authtoken))successBlock
                                    failureBlock:(void (^)(NSString *errorMessage))failureBlock    
{
    successBlock(@"mocked unit test auth token");
}

- (void)testLoginWithUserCallsSomethingInCredStoreOnLoginSuccess
{
    LoginService *loginService = [[LoginService alloc] init];

    // init mocks we need for this test
    id credStoreMock = [OCMockObject niceMockForClass:[MyCredStore class]];
    id loginCtrlMock = [OCMockObject niceMockForClass:[MyLoginCtrl class]];

    // force login controller to call success block when called with loginWithUserPass
    // onObject:self - if mock method is in the same unit test, use self. if it is helper object, point to the helper object.
    [[[loginCtrlMock stub] andCall:@selector(mockSuccessBlockWithOneArgumentTwoBlocks:successBlock:failureBlock::) onObject:self] loginWithUserPass:OCMOCK_ANY withSuccess:OCMOCK_ANY failure:OCMOCK_ANY];

    // setup mocks
    loginService.credStore = credStoreMock;
    loginService.loginCtrl = loginCtrlMock;

    // expect/run/verify
    [[credStore expect] callSomethingFromSuccessBlock];
    [loginService loginWithUser:@"testUser" andPass:@"testPass"];
    [credStore verify];
}


0 commentaires