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]; }
3 Réponses :
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. p>
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 :)
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
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 code> et
cmd code> 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 i> 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);
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]; }