Returning method object from inside block - ios

I am wondering how to do the following correctly: I have a method that is to return an NSData object. It gets the NSData object from a UIDocument. The NSData object can get large, so I want to make sure it is fully loaded before the response starts. I would therefore like to return the value of the method from within the block itself. So something like this:
- (NSData*)getMyData {
MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
if (success) {
return doc.myResponseData; // this is to be the return for the method not the block
}
}];
}
This causes an error because the return apparently refers to the block's return.
How can I accomplish this without having to make a thread blocking wait/while loop?
Thanks.

You can't. Embrace the fact that what you're trying to do is asynchronous and add a completion block parameter to your getMyData method which is called when the inner completion handler is called. (And remove the return from the method signature):
- (void)getMyDataWithCompletion:(void(^)(NSData *data))completion {
MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
completion((success ? doc.myResponseData : nil));
}];
}
The same problem exists in swift and you can add a similar completion block:
func getMyData(completion: ((data: NSData?) -> Void) {
data = ...
completion(data)
}

The open method is asynchronous which is why you have to provide a block to be run when the open is completed. You need to copy this and make your method also receive a block of code that you will execute when the open is finished.
You should also pass through the success argument of the call you are wrapping or create an error, you need to do this so that the calling code can take the right action.
- (void)getMyDataWithCompletion:(void(^)(NSData *data, BOOL success))completion
{
MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
completion(doc.myResponseData, success);
}];
}

Following Are method how to declare method with completionHandler:
Objective-C
- (void)getMyDataWithCompletionHandler:(void(^)(NSString *str))completionHandler
{
completionHandler(#"Test");
}
Swift-3
func showDatePicker(superc: UIViewController, completionHandler:#escaping (String) -> Void) {
completionHandler("Test")
}

Related

ios - How to mock a method with pmkpromise as the return type

I have method which I want to unit test:
- (void)fetchInfo {
[AMKAccountService getInfo]
.then(^(AMKInfoResponse *response) {
if (response.accounts.count > 0) {
_viewModel = [[AMKInfoViewModel alloc] initWithInfoResponse:response];
[self.view setAsOfDate:_viewModel.asOfDate];
} else {
[self.view showError:[AMKStrings feedbackForCode:#"testError"]];
}
}).catch(^(NSError *error) {
DLog(#"Error getting info: %#", error);
[self.view showError:[AMKStrings feedbackForCode:#"testError"]];
});
}
In this method, 'getInfo' method makes a service call and returns response of type PMKPromise object.
My question is how to mock the getInfo method and make the 'then' block called for one unit test and 'catch' block called for the other unit test.
[Update]
Here's getInfo method:
+ (PMKPromise *)getInfo {
AMKServicesClient *client = [AMKServicesClient sharedInstance];
return [client GET:#"/amk-web-services/rest/info" parameters:nil].thenInBackground(^(NSDictionary *responseDictionary) {
return [[AMKInfoResponse alloc] initWithResponse:responseDictionary];
});
}
In order to test this, you will have to either break the dependency between getInfo and the shared AMKServicesClient, or set up some sort of system that allows you to load a mock services client when [AMKServicesClient sharedInstance] is called.

Stubbing blocks in method OCMock

I am facing some hard time in mocking a block inside one of my methods which i want to test.
Below is more or less how my code looks like
- (void) startFetching:(MyParameter *) parameter
{
self.fetcher = [[MyFetcher alloc] initWithContext:xxxx andObserver:nil];
self.fetcher.parameters = #[parameter];
[self.fetcher startWithCompleteionBlock:^(id<MyOperation> _Nonnull operation) {
if(operation.errors.count > 0) {
[self.delegate failedWithError:operation.errors.firstObject];
} else{
FetcherResponse *response = [MyFetcherResponse cast:operation];
NSArray *array = response.responseArray;
if(array.count == 1) {
[self.delegate completedWithSuccess:array.firstObject];
}
}
}];
}
Now i have a test method like testStartFetching and i want to test this method. i don't understand how i can stub this part [self.fetcher startWithCompleteionBlock:^(id<MyOperation> _Nonnull operation) inside my method so that, in success case it return proper array and in failure case it return errors and if i stub it for with errors then failedWithError:operation is called and completedWithSuccess is called otherwise.
I am using OCMock framework in objective c and i am new to unit testing. Any help will be highly appreciated.
I stub method with completion block which returns operation (with errors). Then I verify that calls delegate's method - failedWithError with right parameter (error).
id<MyOperation> operation = [[MyClassOperaion alloc] init];
NSError *error = [NSError new];
operation.errors = #[error];
OCMStub([self.fetcher startWithCompleteionBlock:([OCMArg checkWithBlock:^BOOL(void(^passedBlock)(id<MyOperation> _Nonnull operation)) {
passedBlock(operation);
return YES;
}])]);
OCMVerify([self.delegate failedWithError:error]);

How can I return the correct value in this block in iOS

I am getting a compiler error
incompatible block pointer types sending 'BOOL'
All I am trying to do is return the value in my completion block in the following code:
-(BOOL)registerUserWithUser:(NSString*)name withPhone: (NSString*)number withPassword: (NSString*)password{
GlobalFunctions *function = [GlobalFunctions sharedInstance];
[function registerUserWithName:name phone:number password:password completion:^(BOOL isSuccess, NSError *error) {
return isSuccess;
}];
}
I've read other similar answers but none really solves my issue directly. Is the value returned already before the block is complete?
Is the value returned already before the block is complete?
In short: Yes! Blocks run asynchronously. The return from the block cannot be used as the return for the method.
However, that's not what's causing the compile time error. Your block doesn't have a return type:
^returnType(parameters)
but you are returning something inside the block anyway. Have a look here for more information on block syntax.
Your method is async,so you cannot get return in traditional way. If you return something form block,it is the return for block, not for function.
I think it is better to pass your logic code in callback.
typedef void(^Callback)(BOOL success);
-(void)registerUserWithUser:(NSString*)name withPhone: (NSString*)number withPassword: (NSString*)password callBack:(Callback)callBack{
GlobalFunctions *function = [GlobalFunctions sharedInstance];
[function registerUserWithName:name phone:number password:password completion:^(BOOL isSuccess, NSError *error) {
callBack(isSuccess);
}];
}
Then you can use it like this
[self registerUserWithUser:#"" withPhone:#"" withPassword:#"" callBack:^(BOOL success) {
if (success) {
}else{
}
}];
I think you want to pass the block as a method parameter. You can declare your method like this.
-(void)registerUserWithUser:(NSString*)name withPhone: (NSString*)number withPassword: (NSString*)password successHandler:(void (^)(BOOL isSuccess))myBlock;
Implementation will be somewhat like this. At the end, you can call your success handler block parameter.
-(void)registerUserWithUser:(NSString*)name withPhone: (NSString*)number withPassword: (NSString*)password successHandler:(void (^)(BOOL isSuccess))myBlock{
/***
** Do your logic here **
** Then call the success handler block.
****/
BOOL isSuccess= FALSE;
myBlock(isSuccess);
}
You can call your method like this:
[self registerUserWithUser:#"xyz" withPhone:#"1234" withPassword:#"abcd" successHandler:^(BOOL success){
NSLog(#"success: %d", success);
}];
Hope it helps!!

How do I return a value from a Helper Class to a view controller?

I have a viewcontroller that calls a HelperClass class method in its viewDidLoad like so:
- (void)viewDidLoad{
[super viewDidLoad];
self.usersArray = [SantiappsHelper fetchUsers];
}
That class method looks like this:
+(NSArray *)fetchUsers{
NSString *urlString = [NSString stringWithFormat:#"http://www.myserver.com/myApp/getusers.php"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
[request setHTTPMethod: #"GET"];
__block NSArray *usersArray = [[NSArray alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
// Peform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error) {
// Deal with your error
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"HTTP Error: %d %#", httpResponse.statusCode, error);
return;
}
NSLog(#"Error %#", error);
return;
}
NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSLog(#"responseString fetchUsers %#", responseString);
NSLog(#"inside of block");
usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
});
NSLog(#"outside of block");
return usersArray;
}
The responseString is printed out just fine. But how do I return that value to my view controller? Because its a tableview controller which already loads its tableview before any of the data is gotten.
The actual question is "How is a result returned from an asynchronous method?"
Say, you have an asynchronous task "doSomethingAsync" (which is a class method or instance method or a function, but this doesn't really matter).
The familiar synchronous form "doSomething" would simply return the result and can be declared as follows:
- (Result*) doSomething;
The equivalent asynchronous task "doSomethingAsync" can be declared using a completion handler:
typedef void (^completion_block_t)(Result* result)
- (void) doSomethingAsync:(completion_block_t)completionHandler;
Example:
Suppose a class "MyClass" defines a property "result" which will be initialized from the result of an asynchronous class method (class Foo). You retrieve the result in the method "fetchResult":
- (void) fetchResult {
[Foo doSomethingAsync:^(Result* result){
self.result = result;
}];
}
It may take a while to grasp what's going on here and it requires you to "think asynchronous" ;)
The important thing to realize is, that the completion handler is a block - which is defined inline and treated as if it were a normal object. The block is created by the call site and passed as an argument for the parameter completionHandler to the doSomethingAsync: method. The block itself defines the actions to take when the asynchronous task completes.
On the other hand, the asynchronous method internally must keep a reference to this block until it completes. Then, it must call the block and provide its result as an argument to the parameter result of the completion block.
There are other forms to "return" the result of an asynchronous function. One common pattern is to use a Future or a Promise. A Future or Promise simply represents the eventual result of an asynchronous function. It is an object that can be immediately returned from the asynchronous function - but its value (the result of the asynchronous task) is available only later when the asynchronous task finished. The task MUST eventually set a value for the promise when it finishes. This is called "resolving". That means, that the task must keep a reference to that returned promise object, and finally "resolve" it with either a value meaning success or a value meaning failure.
Assuming there is such a class "Promise", this would let you declare asynchronous methods like this:
- (Promise*) doSomethingAsync;
An implementation of a Promise may be fully support the "asynchronous model". In order to retrieve the result, you simply define what to do when the result is available. A particular implementation of a Promise may accomplish this for example:
- (void) fetchResult {
Promise* promise = [Foo doSomethingAsync];
promise.then(^(Result* result){
self.result = result;
});
}
Notice the "then", which is actually a property of the class Promise that returns a block:
#property then_block_t then;
This returned block of type "then_block_t" is immediately called via:
promise.then(...)
Much like:
then_block_t block = promise.then;
block( ... );
but shorter.
The block of type "then_block_t" has a parameter which is a completion block which will be invoked by the promise when the result is eventually available. The completion block is defined inline:
^(Result* result){ ... }
As you can see, the completion block has a parameter result which is the actual result of the asynchronous method.
OK, now your head may spin ;)
But now, go back to the example
Promise* promise = [Foo doSomethingAsync];
promise.then(^(Result* result){
self.result = result;
});
which simply reads:
"Start asynchronous method [Foo doSomethingAsync] and return a
promise.
When finished then execute the block where the result of task
"doSomethingAsync" is passed with the argument result."
You can it write even shorter:
[Foo doSomethingAsync]
.then(^(Result* result) {
self.result = result;
};
which is akin to the form with the completion handler:
[Foo doSomethingAsync:^(Result* result){
self.result = result;
}];
The most important feature of a Promise, though, is that it allows us to "chain" two or more asynchronous tasks together. This is made possible since the block of type then_block_t which is returned from the property then has a return value of type Promise.
typedef Promise* (^then_block_t)(completion_block_t onSuccess);
I'm pretty sure your head is spinning now in high frequency ;) - thus, an example will make this clear (hopefully):
Suppose you have two asynchronous methods: asyncA and asyncB. The first requires an input, processes it asynchronously and produces a result. The second method asyncB should take this result, process it asynchronously and finally print out #"OK" or an NSError - if something went wrong:
[self asyncA:input]
.then(^(OutputA* outA) {
return [self asyncB:outA];
})
.then(^(OutputB* outB){
NSLog(#"end result: %#", outB);
return nil;
});
This reads:
"Asynchronously perform task "asyncA".
When finished then asynchronously perform task "asyncB".
If finished, then print out result."
You may notice that a handler will return a Promise object in the statement
return [self asyncB:outA];.
This will establish the "chain" form task "asyncA" to "asyncB". The eventual "value" of the returned promise will then appear as the result parameter in next handler.
A handler may also return an immediate result which happens to end up as the result parameter in the next handler as well.
An actual implementation in Objective-C differs slightly, in that the *then_block_t* has two parameters: one for the success case and one for the failure case:
typedef Promise* (^then_block_t)(completion_block_t onSuccess, failure_block_t onFailure);
I left this out in the former samples for brevity. An actual implementation would look like this:
[self asyncA:input]
.then(^(OutputA* out) {
return [self asyncB:out];
}, nil)
.then(^(id result){
NSLog(#"result: %#", result);
return nil;
}, ^id(NSError*error){
NSLog(#"ERROR: %#", error);
return nil;
});
Anther cool feature of promises is that errors will be forwarded through the chain of promises. That means, one can have multiple "chained" tasks say, A, B, C, D where only the success handler is defined. The last handler (pair) defines an error handler. If an error occurs in the first async task - that error will be forwarded through all promises until an error handler finally handles it. Success handlers will only be called when the task succeeded, and the error handler will only be called when the task failed:
[self A]
.then(^(id result) {
return [self B:result];
}, nil)
.then(^(id result) {
return [self C:result];
}, nil)
.then(^(id result) {
return [self D:result];
}, nil)
.then(^(id result) {
NSLog(#"Success");
return nil;
}, ^id(NSError*error){
NSLog(#"ERROR: %#", error);
return nil;
});
There's more regarding Promises, but well beyond this SO answer.
An example for an implementation can be found here: RXPromise
I would suggest the following:
Add the following to SantiappsHelper.h
typedef void (^Handler)(NSArray *users);
In viewDidLoad, change
self.usersArray = [SantiappsHelper fetchUsers];
to
[SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) {
self.usersArray = users;
}];
Change
+(NSArray *)fetchUsers{
to
+(void)fetchUsersWithCompletionHandler:(Handler)handler {
in both .m and .h files.
And in this method, right after
usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
add
if (handler)
handler(usersArray);
Remove
return usersArray;
I think that should do. Also, execute the handler block on the main thread if that's desired.

Testing asynchronous call in unit test in iOS

I am facing a problem while unit testing an asynchronous call in iOS. (Although it is working fine in view controllers.)
Has anyone faced this issue before? I have tried using a wait function but I'm still facing the same problem.
Please suggest an example of a good way to do this.
You'll need to spin the runloop until your callback is invoked. Make sure that it gets invoked on the main queue, though.
Try this:
__block BOOL done = NO;
doSomethingAsynchronouslyWithBlock(^{
done = YES;
});
while(!done) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
You can also use a semaphore (example below), but I prefer to spin the runloop to allow asynchronous blocks dispatched to the main queue to be processed.
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
doSomethingAsynchronouslyWithBlock(^{
//...
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
Here is Apple's description of native support for async testing.
TL;DR manual:
Look at XCTextCase+AsynchronousTesting.h
There is special class XCTestExpectation with only one public method: - (void)fulfill;
You should init instance of this class and in success case call fulfill method. Otherwise your test will fail after timeout that you specify in that method:
- (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout handler:(XCWaitCompletionHandler)handlerOrNil;
Example:
- (void)testAsyncMethod
{
//Expectation
XCTestExpectation *expectation = [self expectationWithDescription:#"Testing Async Method Works Correctly!"];
[MyClass asyncMethodWithCompletionBlock:^(NSError *error) {
if(error)
NSLog(#"error is: %#", error);
else
[expectation fulfill];
}];
//Wait 1 second for fulfill method called, otherwise fail:
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
if(error)
{
XCTFail(#"Expectation Failed with error: %#", error);
}
}];
}
I think many of the suggested solutions in this post has the problem that if the asynchronous operation does not complete the "done" flag is never set, and the test will hang forever.
I have successfully used this approach in many of my test.
- (void)testSomething {
__block BOOL done = NO;
[obj asyncMethodUnderTestWithCompletionBlock:^{
done = YES;
}];
XCTAssertTrue([self waitFor:&done timeout:2],
#"Timed out waiting for response asynch method completion");
}
- (BOOL)waitFor:(BOOL *)flag timeout:(NSTimeInterval)timeoutSecs {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if ([timeoutDate timeIntervalSinceNow] < 0.0) {
break;
}
}
while (!*flag);
return *flag;
}
Since Xcode 6 this built in to XCTest as a category:
See https://stackoverflow.com/a/24705283/88164
Here's another alternative, XCAsyncTestCase, that works well with OCMock if you need to use it. It's based on GHUnit's async tester, but is uses the regular XCTest framework instead.
Fully compatible with Xcode Bots.
https://github.com/iheartradio/xctest-additions
Usage is the same, just import and subclass XCAsyncTestCase.
#implementation TestAsync
- (void)testBlockSample
{
[self prepare];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){
sleep(1.0);
[self notify:kXCTUnitWaitStatusSuccess];
});
// Will wait for 2 seconds before expecting the test to have status success
// Potential statuses are:
// kXCTUnitWaitStatusUnknown, initial status
// kXCTUnitWaitStatusSuccess, indicates a successful callback
// kXCTUnitWaitStatusFailure, indicates a failed callback, e.g login operation failed
// kXCTUnitWaitStatusCancelled, indicates the operation was cancelled
[self waitForStatus:kXCTUnitWaitStatusSuccess timeout:2.0];
}
AGAsyncTestHelper is a C macro for writing unit tests with asynchronous operations and works with both SenTestingKit and XCTest.
Simple and to the point
- (void)testAsyncBlockCallback
{
__block BOOL jobDone = NO;
[Manager doSomeOperationOnDone:^(id data) {
jobDone = YES;
}];
WAIT_WHILE(!jobDone, 2.0);
}
Sam Brodkin already gave the right answer.
Just to make the answer looks better at first sight, I bring the sample code here.
Use XCTestExpectation.
// Test that the document is opened. Because opening is asynchronous,
// use XCTestCase's asynchronous APIs to wait until the document has
// finished opening.
- (void)testDocumentOpening
{
// Create an expectation object.
// This test only has one, but it's possible to wait on multiple expectations.
XCTestExpectation *documentOpenExpectation = [self expectationWithDescription:#"document open"];
NSURL *URL = [[NSBundle bundleForClass:[self class]]
URLForResource:#"TestDocument" withExtension:#"mydoc"];
UIDocument *doc = [[UIDocument alloc] initWithFileURL:URL];
[doc openWithCompletionHandler:^(BOOL success) {
XCTAssert(success);
// Possibly assert other things here about the document after it has opened...
// Fulfill the expectation-this will cause -waitForExpectation
// to invoke its completion handler and then return.
[documentOpenExpectation fulfill];
}];
// The test will pause here, running the run loop, until the timeout is hit
// or all expectations are fulfilled.
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
[doc closeWithCompletionHandler:nil];
}];
}
you can use async api calling in swift like this
private let serverCommunicationManager : ServerCommunicationManager = {
let instance = ServerCommunicationManager()
return instance
}()
var expectation:XCTestExpectation?
func testAsyncApiCall() {
expectation = self.expectation(description: "async request")
let header = ["Authorization":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImQ4MmY1MTcxNzI4YTA5MjI3NWIzYWI3OWNkOTZjMGExOTI4MmM2NDEyZjMyYWQzM2ZjMzY4NmU2MjlhOWY2YWY1NGE0MDI4MmZiNzY2NWQ3In0.eyJhdWQiOiIxIiwianRpIjoiZDgyZjUxNzE3MjhhMDkyMjc1YjNhYjc5Y2Q5NmMwYTE5MjgyYzY0MTJmMzJhZDMzZmMzNjg2ZTYyOWE5ZjZhZjU0YTQwMjgyZmI3NjY1ZDciLCJpYXQiOjE1MDg4MjU1NTEsIm5iZiI6MTUwODgyNTU1MSwiZXhwIjoxNTQwMzYxNTUxLCJzdWIiOiIiLCJzY29wZXMiOltdfQ.osoMQgiY7TY7fFrh5r9JRQLQ6AZhIuEbrIvghF0VH4wmkqRUE6oZWjE5l0jx1ZpXsaYUhci6EDngnSTqs1tZwFTQ3srWxdXns2R1hRWUFkAN0ri32W0apywY6BrahdtiVZa9LQloD1VRMT1_QUnljMXKsLX36gXUsNGU6Bov689-bCbugK6RC3n4LjFRqJ3zD9gvkRaODuOQkqsNlS50b5tLm8AD5aIB4jYv3WQ4-1L74xXU0ZyBTAsLs8LOwvLB_2B9Qdm8XMP118h7A_ddLo9Cyw-WqiCZzeZPNcCvjymNK8cfli5_LZBOyjZT06v8mMqg3zszWzP6jOxuL9H1JjBF7WrPpz23m7dhEwa0a-t3q05tc1RQRUb16W1WhbRJi1ufdMa29uyhX8w_f4fmWdAnBeHZ960kjCss98FA73o0JP5F0GVsHbyCMO-0GOHxow3-BqyPOsmcDrI4ay006fd-TJk52Gol0GteDgdntvTMIrMCdG2jw8rfosV6BgoJAeRbqvvCpJ4OTj6DwQnV-diKoaHdQ8vHKe-4X7hbYn_Bdfl52gMdteb3_ielcVXIaHmQ-Dw3E2LSVt_cSt4tAHy3OCd7WORDY8uek4Paw8Pof0OiuqQ0EB40xX5hlYqZ7P_tXpm-W-8ucrIIxgpZb0uh-wC3EzBGPjpPD2j9CDo"]
serverCommunicationManager.sendServerRequest(httpMethodType: .get, baseURL: "http://192.168.2.132:8000/api/v1/user-role-by-company-id/2", param: nil, header: header) { (isSuccess, msg , response) in
if isSuccess
{
let array = response as! NSArray
if array.count == 8
{
XCTAssertTrue(true)
self.expectation?.fulfill()
}
else
{
XCTAssertFalse(false)
XCTFail("array count fail")
}
}
}
waitForExpectations(timeout: 5) { (error) in
if let error = error{
XCTFail("waiting with error: \(error.localizedDescription)")
}
}
}
I suggest you should have a look on the tests of Facebook-ios-sdk. It's a good example of how to test async unit test on iOS, though personally I think async tests should be break into sync tests.
FBTestBlocker: a blocker that prevent current thread exits with specified timeout. You can drag and drop this to your project, but you need to remove OCMock related stuff if you don't have that in you project.
FBTestBlocker.h
FBTestBlocker.m
FBURLConnectionTests: test examples you should look at.
FBURLConnectionTests.h
FBURLConnectionTests.m
This code snippet should give you some idea
- (void)testExample
{
FBTestBlocker *_blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:1];
__block BOOL excuted = NO;
[testcase test:^(BOOL testResult) {
XCTAssert(testResult, #"Should be true");
excuted = YES;
[_blocker signal];
}];
[_blocker waitWithTimeout:4];
XCTAssertTrue(excuted, #"Not executed");
}
Try KIWI framework. It's powerful and might help you with other kinds of tests.
I recommend you connection semaphore + runloop, i also wrote method which take block:
// Set the flag to stop the loop
#define FLEND() dispatch_semaphore_signal(semaphore);
// Wait and loop until flag is set
#define FLWAIT() WAITWHILE(dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
// Macro - Wait for condition to be NO/false in blocks and asynchronous calls
#define WAITWHILE(condition) \
do { \
while(condition) { \
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; \
} \
} while(0)
method:
typedef void(^FLTestAsynchronousBlock)(void(^completion)(void));
void FLTestAsynchronous(FLTestAsynchronousBlock block) {
FLSTART();
block(^{
FLEND();
});
FLWAIT();
};
and call
FLTestAsynchronous(^(void(^completion)()){
[networkManager signOutUser:^{
expect(networkManager.currentUser).to.beNil();
completion();
} errorBlock:^(NSError *error) {
expect(networkManager.currentUser).to.beNil();
completion();
}];
});
If you are using XCode 6, you can test async network calls like this:
XCTest and asynchronous testing in Xcode 6

Resources