2

I have a unit test for a method that should call either a completion block or a failed block. Now I know for every case which one should be called so I use STFail in the block that should not be invoked.

How can I now test that the block that should be invoked is really invoked?

This is my setup:

NSString *parameter = @"foo";
[controller doSomethingWithParameter:parameter withcompletionBlock: 
^(NSString *result)
{
    // This block should be invoked
    // Check if the result is correct
    STAssertEquals(result, kSomeOKConstant, @"Result shout be 'kSomeOKConstant'");
} failedBlock:
^(NSString *errorMessage) {
    STFail(@"No error should happen with parameter '%@'",parameter);
}];
Besi
  • 22,579
  • 24
  • 131
  • 223

1 Answers1

3

You need to add block variables, and set them from inside your blocks:

BOOL __block successBlockInvoked = NO;
BOOL __block failureBlockInvoked = NO;
NSString *parameter = @"foo";
[controller doSomethingWithParameter:parameter withcompletionBlock: 
^(NSString *result) {
     successBlockInvoked = YES;
     STAssertEquals(result, kSomeOKConstant, @"Result shout be 'kSomeOKConstant'");
} failedBlock:
^(NSString *errorMessage) {
    failureBlockInvoked = YES;
    STFail(@"No error should happen with parameter '%@'",parameter);
}];

At this point you can make assertions about the values of successBlockInvoked and failureBlockInvoked: if the expected one is not set, your test has failed.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • But where can I do this assertion. If I do it after the call to `doSomethingWithParameter` then it will be set to `NO` anyway, right? – Besi Jan 27 '12 at 12:01
  • @Besi no, once it is set in the block, it will remain set to YES even after doSomethingWithBlock returns. That is the "magic" of __block variables. – Sergey Kalinichenko Jan 27 '12 at 12:23
  • No way?! Now I am both confused and excited :-) I'll give it a shot, thanks in advance. – Besi Jan 27 '12 at 12:27
  • I'm confused.If you assert right after doSomething is called your successBlockInvoked boolean will not be set unless the block happened to get called synchronously, which I guess could happen sometimes, but I don't see this as a general solution. You would need to have a while loop waiting for the block to get called and a timeout in case it never does. I think, no? – eddy Feb 01 '13 at 17:42
  • @eddy Correct, the code snippet assumes that the block is executed synchronously in the testing environment. It should be OK for quick demos, but obviously one needs to implement *some* waiting mechanism in real-life (i.e. non-testing) environments. – Sergey Kalinichenko Feb 01 '13 at 17:48
  • Is there any way without introducing a boolean? – fatuhoku Sep 26 '14 at 13:00
  • @fatuhoku No, it is not possible, because you would not be able to distinguish between a situation when your block successfully passed an assertion and a situation when your block did not assert anything at all, because it has not been invoked. – Sergey Kalinichenko Sep 26 '14 at 13:05