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]);
Related
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.
I have this code:
- (NSString *) login {
datos=#"";
NSString __block *variable;
NSString *sqlQueryExisteUsuario;
sqlQueryExisteUsuario = [[NSString alloc] initWithFormat:#"SELECT COUNT(*) FROM tableName WHERE field='value' AND field='value'"];
SQLClient* client = [SQLClient sharedInstance];
client.delegate = self;
[client connect:#"serverName" username:#"username" password:#"password" database:#"database" completion:^(BOOL success) {
[client execute:sqlQueryExisteUsuario completion:^(NSArray* results) {
variable = [self processLogin:results];
NSLog(#"In: %#",variable);
[client disconnect];
}];
}];
NSLog(#"Out: %#",variable);
return nil;
}
- (NSString *)processLogin:(NSArray*)data
{
existeArray = [NSMutableArray array];
for (NSArray* table in data)
for (NSDictionary* row in table)
for (NSString* column in row)
[existeArray addObject:row[column]];
NSString *existe=existeArray[0];
if([existe isEqualToString:#"1"])
{
datos=#"yes";
}else{
datos=#"no";
}
return datos;
}
In the first call to NSLog, which begins with In, the value shows. In the second call, which begins with Out, the value doesn't show. Why?
Your connect is async method, so NSLog... line will be executed earlier than completion block. So, you have to use blocks also:
- (NSString *) loginWithCompletion:(void(^)(NSString *result))handler
{
datos=#"";
NSString *sqlQueryExisteUsuario;
sqlQueryExisteUsuario = [[NSString alloc] initWithFormat:#"SELECT COUNT(*) FROM tableName WHERE field='value' AND field='value'"];
SQLClient* client = [SQLClient sharedInstance];
client.delegate = self;
[client connect:#"serverName" username:#"username" password:#"password" database:#"database" completion:^(BOOL success) {
if (success) {
[client execute:sqlQueryExisteUsuario completion:^(NSArray* results) {
NSString *variable = [self processLogin:results];
NSLog(#"In: %#",variable);
[client disconnect];
if (handler) {
handler (variable);
}
}];
} else {
//TODO: handle this
if (handler) {
handler (nil);
}
}
}];
}
Usage:
- (void)ff
{
[self loginWithCompletion:^(NSString *variable) {
//Do something with variable
}];
}
The problem is that your variable is being set inside a completion block: the variable variable (not a great name, BTW!) is set inside not only one but two blocks - that's the "completion" part of your code – both of which are best thought of a bit(!) like a miniature anonymous function: in this case, they get run when the system is ready for it, not straight away.
iOS will start execution of your connect code, then jump ahead to NSLog(#"Out: %#",variable), then execute the completion block of connect, which in turn starts more code (execute), which has its own completion block, which finally gets executed. As #rmaddy says in a comment below, the technical name for this is asynchronous code: the bit inside your block does not get executed immediately, which allows the system to continue doing other things while waiting for your task to complete.
So the running order will be:
1) You declare variable.
2) Your connection code starts.
3) variable gets printed out.
4) The connection completes.
5) Your execute code starts.
6) Your execute code completes.
7) variable gets set to the final value.
How to check passed callback block is called from tested method? Here's simplified method I want to test:
- (void)loadModel:(Callback)callback {
// Data loading part...
// Callbacks with data. Second param usually passes data, but now it will pas nil for demo
callback(YES, nil, nil);
}
Here's my unit test where I want to test if callback was called in method loadModel:
- (void)testLoadModelCallbackIsCalled {
HomeModel *model = [[HomeModel alloc] init];
id modelMock = OCMPartialMock(model);
Callback callback = ^(BOOL success, id result, NSError *error){
XCTAssertTrue(YES, #"Callback not called");
};
//[modelMock expect] ...how to expect?];
[model loadModel:callback];
//[modelMock verify];
}
The problem is this test will always pass regardless of callback was called on not. Thought to workaround this with proxy block, however then the whole loadModel method is overridden with the proxy block. I just need to verify loadModel actually calls what I pass.
UPDATE Meanwhile I ended in simplified workaround:
- (void)testLoadModelCallbackIsCalled {
HomeModel *model = [[HomeModel alloc] init];
// Create flag for checking
__block BOOL isCalled = NO;
Callback callback = ^(BOOL success, id result, NSError *error){
isCalled = YES;
};
XCTAssertTrue(isCalled, #"Callback not called");
}
UPDATE2 #jszumski answer was exactly what I was looking for thought the idea behind XCTestExpectation is very similar to using your own isCalled bool flag.
Use XCTestExpectation, which is part of the asynchronous testing framework:
- (void)testLoadModelCallbackIsCalled {
HomeModel *model = [[HomeModel alloc] init];
id modelMock = OCMPartialMock(model);
XCTestExpectation *expectation = [self expectationWithDescription:#"Completion called"];
Callback callback = ^(BOOL success, id result, NSError *error){
[expectation fulfill];
};
[model loadModel:callback];
[self waitForExpectationsWithTimeout:1 handler:nil];
}
I am starting in Unit testing with objective-c and I need to know how to test blocks with OCMockito and Xcode 6.
I am testing an Interactor, this interactor should return an array as a block argument and I has to ask the Provider file for the elements.
This is the method I want to test:
- (void)userPoiListsWithSuccessBlock:(MNBSavePoisInteractorSuccess)success {
self.poiListEntityArray = [self.poiListProvider poiListsForUser:self.loggedUser];
self.poiListViewObjectArray = [self viewPoiListObjectListWithPoiLists:self.poiListEntityArray];
success(self.poiListViewObjectArray);
}
First, I setup the elements that I am going to use
self.mockPoiListProvider = mock([PoiListProvider class]);
self.sut = [[MNBSavePoisInteractor alloc] initWithManagedObjectContext:self.coreDataStack.managedObjectContext andPoiListProvider:self.mockPoiListProvider];
- (UserEntity *)loggedUserMock {
UserEntity *mockLoggedUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([UserEntity class]) inManagedObjectContext:self.coreDataStack.managedObjectContext];
mockLoggedUser.userId=#"1";
mockLoggedUser.username=#"user";
mockLoggedUser.loggedUser=#YES;
return mockLoggedUser;
}
- (InMemoryCoreDataStack *)coreDataStack{
if (!_coreDataStack) {
_coreDataStack = [[InMemoryCoreDataStack alloc] init];
}
return _coreDataStack;
}
- (PoiListEntity *)poiListFake {
PoiListEntity *fake = [NSEntityDescription insertNewObjectForEntityForName:#"PoiListEntity" inManagedObjectContext:self.coreDataStack.managedObjectContext];
fake.name = #"Test";
fake.poisCount = #2;
[fake addContributorsObject:[self loggedUserMock]];
return fake;
}
Then, I do the test. I am using Xcode 6 waitForExpectation to manage the asynchronous methods. I think I am doing something wrong.
- (void)waitForExpectation {
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
if (error) {
NSLog(#"Timeout Error: %#", error);
}
}];
}
- (void)testShouldReturnPoiLists {
XCTestExpectation *expectation = [self expectationWithDescription:#"Waiting method ends"];
[given([self.mockPoiListProvider poiListsForUser:[self loggedUserMock]]) willReturn:#[[self poiListFake]]];
[self.sut userPoiListsWithSuccessBlock:^(NSArray *results) {
[expectation fulfill];
XCTAssert(resutls.count == 1, #"Results %zd", resutls.count);
}];
[self waitForExpectation];
}
I understood if I give the object in willReturn in the given method, when I call the sut method that I want to test it should return what I give before. Is that true?
Thank you
I see no asynchronous code. You just want a block that captures the results, so use a __block variable to make the results available outside of the block. Then you can assert whatever you want:
- (void)testShouldReturnPoiLists {
[given([self.mockPoiListProvider poiListsForUser:[self loggedUserMock]]) willReturn:#[[self poiListFake]]];
__block NSArray *capturedResults;
[self.sut userPoiListsWithSuccessBlock:^(NSArray *results) {
capturedResults = results;
}];
assertThat(capturedResults, hasLengthOf(1));
}
The relationship between the length of 1 and the fake is hard to tell. Let's also parameterize the faking code:
- (PoiListEntity *)poiListFakeWithName:(NSString *)name count:(NSNumber *)count {
PoiListEntity *fake = [NSEntityDescription insertNewObjectForEntityForName:#"PoiListEntity" inManagedObjectContext:self.coreDataStack.managedObjectContext];
fake.name = name;
fake.poisCount = count;
[fake addContributorsObject:[self loggedUserMock]];
return fake;
}
With that, we can write more interesting tests.
I do want to add that it's important to "listen to the tests." There's a lot of convoluted set-up to dance around Core Data. That tells me that if you can rewrite things to be independent of Core Data — completely ignorant of it — everything will be much simpler.
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.