2013-05-13 4 views
26

assumere una firma di metodo come il seguente:È possibile che OCMock esegua un parametro di blocco?

- (void)theMethod:(void(^)(BOOL completed))completionBlock; 

vorrei prendere in giro questa firma metodo per assicurare il metodo viene chiamato, e basta chiamare il blocco di completamento. Vedo da altri post come this one che posso prendere in giro la chiamata al metodo e accettare qualsiasi blocco, ma non eseguire il blocco. So anche che c'è un metodo andDo che potrei essere in grado di usare, ma non riesco a capire come passare un blocco ed eseguirlo.

Qualche idea?

Grazie.

risposta

31

È possibile utilizzare [[mock stub] andDo:] come questo per passare un altro blocco che viene chiamato quando il metodo preso in giro si chiama:

void (^proxyBlock)(NSInvocation *) = ^(NSInvocation *invocation) { 
    void (^passedBlock)(BOOL); 
    [invocation getArgument: &passedBlock atIndex: 2]; 
}; 
[[[mock stub] andDo: proxyBlock] theMethod:[OCMArg any]]; 

Il blocco riceve un'istanza NSInvocation da cui è possibile interrogare tutti gli argomenti utilizzati. Si noti che il primo argomento è all'indice 2 poiché si ha self e _cmd agli indici 0 e 1.

+4

E, presumibilmente, faresti passedBlock (SI) nella andDo per chiamare il blocco. –

+0

Grazie!Alla fine ha funzionato, si prega di scrivere il codice per chiamare nel campione. passedBlock (YES) – Denis

16

MODIFICA 2: Utilizzare invece https://stackoverflow.com/a/32945785/637641.

Utilizzare andDo: è perfettamente a posto, ma personalmente preferisco [OCMArg checkWithBlock:].

[[mock expect] theMethod:[OCMArg checkWithBlock:^BOOL(id param) 
{ 
    void (^passedBlock)(BOOL) = param; 
    // Normally I set some expectations here and then call the block. 
    return YES; 
}]]; 

// Code to test 

[mock verify]; 

È possibile utilizzare anche [mock stub] ma preferisco verificare che venga chiamato theMethod.

EDIT 1

OCMock 3 Versione:

OCMExpect([mock theMethod:[OCMArg checkWithBlock:^BOOL(void (^passedBlock)(BOOL)) 
{ 
    // call the block... 
    return YES; 
}]]); 

// Code to test 

OCMVerify(mock); 
+3

Ci sono due problemi nell'utilizzo di checkWithBlock per questo scopo. Uno è che la versione corrente di OCMock smetterà di controllare i parametri dell'oggetto se 'ignoringNonObjectArgs' è true e un parametro non object viene prima del parametro del blocco. Il secondo problema è che se un vincolo di argomento fallisce dopo la chiamata checkWithBlock, il blocco di completamento verrà erroneamente chiamato. L'unico metodo affidabile per chiamare un blocco è usare 'andDo:'. Questo è un peccato, perché 'checkWithBlock' evita fastidiosi parametri di conteggio delle mani per NSInvocation. – phatmann

8

Questo è ora supportato in OCMock 3.2. È possibile utilizzare [OCMArg invokeBlock] e [OCMArg invokeBlockWithArgs:...] per richiamare il blocco passato come argomento a un metodo di stub.

+1

Sarebbe molto utile se potessi fornire un esempio di come li useresti. – singingAtom

+1

@singingAtom check out [i test OCMock] (https://github.com/erikdoe/ocmock/blob/master/Source/OCMockTests/OCMockObjectTests.m#L618-L711). Ho anche messo alcuni esempi su Github [qui] (https://github.com/SteveFortune/ocmock-block-tests) - questi sono un po 'obsoleti, ad es. 'OCMDefault' è cambiato in' [OCMArg defaultValue] '. – sf13579

+0

Questa è di gran lunga la migliore risposta, in quanto evita il mal di testa associato a "NSInvocation". –

5

L'utilizzo dei blocchi andDo: talvolta è necessario ma per la maggior parte dei casi è possibile utilizzare [OCMArg invokeBlock] o [OCMArg invokeBlockWithArgs:].

Nel tuo esempio è possibile effettuare le seguenti operazioni
Se non si preoccupano gli argomenti:

// Call block with default arguments. 
OCMStub([mock theMethod:[OCMArg invokeBlock]]; 

Se si desidera inviare argomenti specifici:

// Call block with YES. 
OCMStub([mock theMethod:([OCMArg invokeBlockWithArgs:@YES, nil])]; 

nota la cessazione nil dato che puoi passare più argomenti a questo metodo. Inoltre l'intera espressione deve essere racchiusa tra parentesi.

Ulteriori informazioni a riguardo sono disponibili nello OCMock documentation.

2

Questa è la risposta di Sven aggiornato per OCMock 3.

OCMStub([myMock myMethodWithMyBlock:[OCMArg any]]).andDo(^(NSInvocation *invocation) { 
    void (^passedBlock)(BOOL myFirstArgument, NSError *mySecondArgument); 
    [invocation getArgument: &passedBlock atIndex: 2]; 
    passedBlock(YES, nil); 
});