As far as I can see, there is no way to verify the order of method invocations on a mock.
Or am I missing something?
- (void)testResetCameraState_resetsCameraView
{
// Arrange
[given([_cameraManagerMock previewLayer]) willReturn:_testLayer];
// Act
[_cameraInteractor resetCameraState];
// Assert
[verifyCount(_cameraViewMock, times(1)) resetPreview];
[verifyCount(_cameraViewMock, times(1)) setPreviewLayer:_testLayer];
}
In this case you cannot verify, that the setPreviewLayer: is called after resetPreview.
I think I found a solution.
It's based on the givenVoid method added in this pull request:
https://github.com/jonreid/OCMockito/pull/93
Sadly it is not merged yet, so you need to download and build this version by yourself:
https://github.com/lysannschlegel/OCMockito/tree/given_void
With the new method you can verify the order of method calls in the following way:
- (void)testResetCameraState_resetsCameraView
{
// Arrange
[given([_cameraManagerMock previewLayer]) willReturn:_testLayer];
[givenVoid([self->_cameraViewMock resetPreview]) willDo:^id (NSInvocation *invocation)
{
[(MKTBaseMockObject*)self->_cameraViewMock reset];
return nil;
}];
// Act
[_cameraInteractor resetCameraState];
// Assert
[verifyCount(_cameraViewMock, never()) resetPreview];
[verifyCount(_cameraViewMock, times(1)) setPreviewLayer:_testLayer];
}
This will reset the mock after the first call of resetPreview.
So we can verify stuff after that call:
resetPreview is never called after the first call.
setPreviewLayer is called after resetPreview.
The reset call also resets the givenVoid() willDo: so a second reset call wouldn't reset the mock again.
Hope this helps, happy coding :D
Related
I have a method that calls a long running process. the long running process and I use AFNetworking which itself uses blocks and returns success block and failure block. So I am trying to test my method and the tests will fail before the success block is called. I thought I would try to get my method to also use blocks. I have another method that I managed to change to use blocks but that only uses bool isFinished and the return value is void. The method I have difficult with needs to return NSDecimalNumber* and takes an NSString.
Method signature
(NSDecimalNumber*) getRate:(NSString*) rateCode;
I would like to be able to able to add a completion block with a BOOL that I set when the AFNetworking method enters the success block
I would also like to be able to call the method and within it's completion block access the NSDecimalNumber* value it returned
Possible? If so please show me how
You probably have to split it apart.
You can have a fetchRate: method that takes a completion block:
- (void)fetchRate:(NSString*)rateCode completion:(void (^)(NSDecimalNumber *))completion;
Then call like this:
void (^completion)(NSDecimalNumber *) = ^(NSDecimalNumber * rate){
// this is called when rate is returned from your webservice
}
// call fetchRate: now, results will arrive later...
[ myObj fetchRate:<rate code> completion:completion ];
// code here runs immediately; the results come back later.
-fetchRate: looks something like...
- (void)fetchRate:(NSString *)rateCode completion:(void (^)(NSDecimalNumber *))completion
{
void (^asiCompletionBlock)(/*args*/) = ^(/*...args...*/){
// called after ASI request completes
NSDecimalNumber * answer = /* get answer from ASI HTTP response */
// call our completion block that was passed in:
completion( answer );
};
// do your asi HTTP request here, pass asiCompletionBlock for completion arg
}
I have a method which calls itself:
-(void)myMethod
{
//do stuff
[self myMethod];
//do stuff
}
I need to check, from inside myMethod where it is being called from. For example, IF called myMethod do this, ELSE do this.
Can you just pass in a boolean to show called from external vs called from recursion?
-(void)myMethod:(bool)externalCall
{
//do stuff
[self myMethod:false];
//do stuff
}
And then call that from outside with:
[self myMethod:true];
That may be over simplifying, especially if you need to get the calling method from multiple different locations (instead of recursion vs external call), but it seems to me the simplest answer to your presented problem.
I have a method that inits the object and it has a completion block: typedef void(^initCompletionHandler)(BOOL succesful);
In this method I want to call the handler but I am not sure how to do it because if I call it before the return the object won't be finished initialising which is used immediately in the next line. I also obviously can't call the handler after the return. i,e:
if(haveError){
handler(NO);
}
else{
handler(YES);
}
return self;
Is there any way I can return and call the handler at the same time?
A couple of observations:
I'm unclear as to why you say "because ... the return object won't be finished initialising." You're doing the initialization, so just ensure it finishes all of the associated initialization before calling that handler. If the issue is that the caller won't have a valid reference to that object yet, you could always include a reference to it in the parameter of the block, e.g.
typedef void(^initCompletionHandler)(MyObject object, BOOL succesful);
and then supply that parameter, e.g.:
if (haveError){
handler(self, NO);
} else {
handler(self, YES);
}
Also, you say "I obviously can't call the handler after the return". But you can. You could just do a dispatch_async, if you wanted:
dispatch_async(dispatch_get_main_queue(), ^{
if (haveError){
handler(NO);
} else {
handler(YES);
}
});
return self;
That's a little inelegant, as if you call it from another thread, you have some potential race conditions that you might have to coordinate/synchronize, but you get the idea: You don't have to call the handler synchronously.
Having made both of those observations, I must confess that I'm not a fan of having init actually launching some asynchronous process and having its own completion block. I'd be inclined to make those two different steps. If you look at the Cocoa API, Apple has largely shifted away from this pattern themselves, generally having one method for instantiation, and another for starting the asynchronous process.
I've got a function downloading something from the internet. Then it maps the items and returns everything to the controller. The problem is that there may be a lot of items (facebook don't let to divide them) and i have to map them in the background. However the user can quit the controller already and i don't know how to check if my success() owner still exists.
Is it a good solution to put the mapping in the dispatch at all...?
Call:
[[MyHTTPClient sharedInstance] getSomethingWithAbc:abc successBlock:^{
[self reloadMyView];
} failureBlock:^(NSError *error) {
//nothing atm
}];
where:
#interface MyHTTPClient : AFHTTPClient
+ (OHTTPClient *)sharedInstance;
- (void)getSomethingWithAbc:(ABC *)abc successBlock:(void (^)())success failureBlock:(void (^)(NSError *error))failure;
Method implementation:
- (void)getSomethingWithAbc:(ABC *)abc successBlock:(void (^)())success failureBlock:(void (^)(NSError *error))failure {
// request something from facebook API, wait for response then run:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// here map these items - there may be a lot of them so i have to do it in background
dispatch_async(dispatch_get_main_queue(), ^{
success(); // this may call a function from controller that does not exist already
});
}
However the user can quit the controller already and i don't know how
to check if my success() owner still exists.
What do you mean if the owner "still exists"?
self is retained by the block. That means that the "owner" will "still exist" as long as the block exists. So if you call success() and the controller is no longer showing, well, it will reloadMyView, which (depending on what your functions do) will "reload" a view that is no longer showing. Which probably doesn't matter.
What if you want to not call reloadMyView at all? Well, you can let the block capture a weak reference to the "owner". That won't prevent it from being deallocated as it normally would. Then, when you call the success block, and the object is deallocated, the weak reference you are using will have value nil, and sending a message to nil does nothing.
You can choose which way you want.
This is fine, but you should have a cancel operation so that, before calling success, you can check if the operation has been cancelled.
You'd better write a network processing module is independent of your controller,and set delegate method as
- (void)didSuccessWithResult:(NSDictionary*)result;
- (void)didFailWithError:(NSError*)error;
or Send Notification
In you network processing module you can define download work with MyHTTPClient to complete the proxy method
Hope helpful
I want to use blocks as callback handler, but I am not sure what I am doing is proper or not because my app is crashing.
Here is what I am doing:
In my FirstViewController I am calling method of class FirstModel to get data from server as follows:
//In FirstViewController.m
[aFirstModelObj retreiveDataWithCallBackHandler:^(NSDictionary *responseDict){
//Data is received so we can proceed...
}];
//In FirstModel.m
typedef void(^newBlock)(NSDictionary *);
newBlock theBlock;
-(void)retreiveDataWithCallBackHandler:(void(^)(NSDictionary *))aBlock
{
//Saving "aBlock" for further use..
theBlock = aBlock;
//Server Processor will retrieve data using URL asynchronously,
//initializing ServerProcessor object and providing FirstModel its delegate,so that when data is received in ServerProcessor class FirstModel's receivedResponse method will get called.
serverProcessorObj.delegate = self;
}
-(void)receivedResponse:(NSDictionary *)responseDict
{
//once data is received call block,
theBlock(responseDict);
}
My app is crashing because I am loosing delegate. When I call [delegate receivedResponse:response] from ServerProcessor it says exc bad access. Can anyone tell what I am doing wrong?
Thanks in advance!
Besides checking that the block is not nil (as per comments), perhaps it has something to do with:
The above approach will only allow you to process one asynchronous request at a time. Are you trying to process concurrent requests? A subsequent request will override the block for a previous request. This could cause problems.
If you need to support concurrent requests, you'll need retain each of the blocks for a currently running request, so that the can be invoked upon completion of the request.
If you were passing on object in, such as:
retreiveDataForCustomer:(Customer*)customer onSuccess:(void (^)(NSDictionary*))success
. . . then I would suggest create a property or associative reference on the customer object to retain the block. However as your call has no parameters, you'll have to find another way of tracking which block goes with which delegate invocation.
I've corrected your code.
-(void)retreiveDataWithCallBackHandler:(void(^)(NSDictionary *))aBlock
{
//Saving "aBlock" for further use..
theBlock = Block_copy(aBlock);
//Server Processor will retrieve data using URL asynchronously,
//initializing ServerProcessor object and providing FirstModel its delegate,so that when data is received in ServerProcessor class FirstModel's receivedResponse method will get called.
serverProcessorObj.delegate = self;
}
-(void)receivedResponse:(NSDictionary *)responseDict
{
//once data is received call block,
if (theBlock)
theBlock(responseDict);
}