Verify method call with a handle argument on an OCMockito mock - objective-c

I have an OCMockito mock of a class QuestionBuilder with the method questionsFromJSON:error:. This method accepts a handle (NSError **)error as an argument. How do I verify the method was called?
I’ve tried:
[verify(builder) questionsFromJSON:#"Fake JSON"
error:nil];
and:
NSError *err;
[verify(builder) questionsFromJSON:#"Fake JSON"
error:&err];
Both issue the error:
testQuestionJSONIsPassedToQuestionBuilder
(QuestionCreationTests) failed:
*** -[NSProxy doesNotRecognizeSelector:questionsFromJSON:error:] called!

I don't think OCMockito supports this yet; when I do it using given instead of verify, i get a weird error when the code under test calls the method with the ** argument. If possible you may have to modify your method signature to not take in an NSError** (if you have control over that code).

Related

Checking the value of a Swift variable inside the completion block of the method that initializes it

Take the creation of NSURLSessionDownloadTask in Objective-C:
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithURL:[NSURL URLWithString:#"google.com"] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (task.state == NSURLSessionTaskStateCompleted) {
// Do things
}
}];
[task resume];
I can access the very task I'm creating, task, within the completion block as well without issue.
However, in Swift, if I try the same thing:
let URL = NSURL(string: "google.com")
let task = NSURLSession.sharedSession().downloadTaskWithURL(URL, completionHandler: { location, response, error in
if task.state == .Completed {
// Do things
}
}
task.resume()
I can an error that "variable is being used within its own initial value".
How do I circumvent this?
Updated answer
I realize now that I didn't read your code carefully. You are not passing the closure to an initializer, but to a method. What I originally wrote is still valid when passing a closure to an initializer, but not in your case.
Your problem is similar though.
You have a task variable, which is initialized with the return value of a function. You pass a closure to the function, and inside the closure you are referencing the task variable.
The compiler doesn't know when the closure is executed (at least I don't think it checks for that, it's a downloadTaskWithURL() internal implementation detail) - and it's possible that it is executed in the function body (as opposed to have it stored in a property and executed at a later time). If the closure is executed in the function body, then it would access to the task variable when it has not been assigned a value yet (because the function is still executing).
If there were a way to let the compiler know the closure is not executed in the function body, then it would be possible for the compiler to handle that case. But swift doesn't implement anything like that.
Conclusion: I appreciate the compiler throwing an error for that, because otherwise I'd expect a runtime exception - although maybe not in your specific case (because the closure is executed later).
Original answer
As you probably know, in swift self is unavailable until all class/struct properties have been properly initialized, and a base initializer has been called (for inherited classes)
You are passing the closure to the class initializer - the compiler can't determine when the closure will be executed, so the closure itself cannot contain any (direct or indirect) reference to self.
In your case, task is the variable being instantiated, so when you are using it in the closure, it's like you are using self. That's not allowed in swift, so you have that error.
The same doesn't happen in Objective C because there's no such constraint in initializers.
Note however that conceptually what you are doing doesn't look a good practice. You are reading properties of a class instance before it's been properly initialized. To determine the status of the call, you should rely on the parameters passed to the closure, which ideally should provide all info you need.
Your task.state has not been initiazed yet whith some value. You are trying to read properties of a class instance before it's been initialized.
Right now the only way I have been able to get around this is to declare the variable before the initializer and then access it inside the closure:
let URL = NSURL(string: "google.com")
var task: NSURLSessionTask?
task = NSURLSession.sharedSession().downloadTaskWithURL(URL, completionHandler: { location, response, error in
if task?.state == .Completed {
// Do things
}
}
task?.resume()

Objective C Unsure where method is being called?

Showing what a novice I am with Objective C here. The second of these two methods is getting called by the method above. Though I have absolutely no idea where? I want to be able to wrap the part that calls the second method in an if statement to determine if the file did exist based on the returned Boolean. Example code would be appreciated, if anyone could also explain how this second method gets called that would also be fantastic.
-(void) queryResponseForURL:(NSURL *)inURL {
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:inURL];
[request setHTTPMethod:#"HEAD"];
NSURLConnection * connection = [NSURLConnection connectionWithRequest:request delegate:self];
// connection starts automatically
}
-(BOOL)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
if([(NSHTTPURLResponse *)response statusCode] == 200){
NSLog(#"file exists");
return YES;
}else return NO;
}
It is getting called by the NSURLConnection object, so you can't intervene in the code that calls it. But you shouldn't need to - the purpose of a delegate method is to enable you to hook in to that inaccessible code.
The method signature is this, by the way:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
It doesn't have a return value and you can't change the method signature, as it's part of the NSURLConnection object's protocol. Remember, you don't call this method, the connection object does. But you can respond to the method. Instead of attempting to return a BOOL, you can deal with your 'if file..' code inside this method, or call another method from it. You should bear in mind that this is triggered (asynchronously) when a response is confirmed to have been initiated - so you can check HTTP header info such as mime type - but it doesn't guarantee a completed data transfer (there are other delegate methods for that).
See also the developer guide to NSURLConnection
The second method is a delegate or callback method. The callback method is called when your connection instance receives a response from the server your connecting with. NSURLConnection has a few informal protocols on NSObject. This means that methods defined in these protocols will automatically be called when any subclass of NSObject has implemented these methods. Your subclass of NSObject has implemented one of these callback methods, therefore this method is called. Most delegates are declared as formal protocols and need to be explicitly defined in your classes in order to make use of them. The informal protocols of NSURLConnection are exception to the rule, though this is about to change in future versions of iOS.
P.S.: a protocol in Objective-C is essentially the same as an interface in Java / C#.
More info here: http://developer.apple.com/library/ios/ipad/#documentation/Foundation/Reference/NSURLConnectionDataDelegate_protocol/Reference/Reference.html
I guess you want to cancel the connection when you receive the wrong response? What you should do is the following:
Store your URLConnection in an ivar or property (personally I'd prefer a property).
In your -connection:didReceiveResponse delegate method, if the response status code isn't an appropriate value, cancel your connection. You can use your ivar or property for this purpose.

How to convert a delegate-based callback system into block-based?

I have a class, which has a delegate based system for sending different type of requests. it uses delegate to tell the object when the request is complete and also if it was a success o an error.
Now, I also have to check what type of request was it in response to take appropriate action.
I have wrapper class that should give me a block based interface for the same.
I pass a completion-block and an error-block to a request method which should internally use this delegate based class.
And when the response comes, should automatically call the appropriate handler for that request type and depending on success and error as well.
I saw a similar question on SO, but it was a little unclear to me, So please give a general idea of how to go about it instead of marking it as duplicate straight away.
Here is one way to do it. Use this RAExpendable class to dynamically build a delegate with a block based implementation.
Let's say your delegate is:
#protocol XDelegate
-(void) foo:(id)response;
#end
Add RAExpendable.h, RAExpendable.m from https://github.com/evadne/RAExpendable to your project. Dynamically add the delegate method:
RAExpendable *expendable = [RAExpendable new];
[expendable addMethodForSelector:#selector(foo:) types:"v#:#" block:^(id x, SEL sel, id response){
NSLog(#"response is %#", response);
}];
And set the expendable class as your delegate:
someObject.delegate = expendable;
Now, if you do this:
[expendable performSelector:#selector(foo:) withObject:#"OK"];
You get the string response is OK. Replace NSLog with whatever success/failure implementation you see fit. From now on, when you call foo:, the block executes instead.
If you want to modify this for your use case, note that the parameters for this example were v#:#, which according to the Type Encoding guide of the runtime means: void return, self, SEL, object. self and SEL are the two hidden parameters present on every Objective-C methods, the third parameter is the first non hidden parameter of the method. The signature of the block has to match the signature of the method.
With REKit, you can make a delegate dynamically like below:
id dynamicDelegate;
dynamicDelegate = [[NSObject alloc] init];
[dynamicDelegate respondsToSelector:#selector(foo:) withKey:nil usingBlock:^(id receiver, id response) {
NSLog(#"response is %#", response);
}];
someObject.delegate = dynamicDelegate;

Stub Methods with Block Arguments

I'm trying to stub a method which has got an block argument.
[[[myMock stub] andCall:#selector(performBlock:) onObject:self] performBlock:[OCMArg any]];
The block is of a simple type.
typedef void (^MyBlock)(void);
Upon execution I get the following error:
unexpected method invoked: performBlock:<__NSStackBlock__: 0xbffff418>
stubbed: performBlock:<OCMAnyConstraint: 0x1c1ff70>
It seems as a block argument is not compatible with [OCMArg any]. Any tipps how to stub this method then?
Ok, I have cropped too much from my example.
I had a second parameter of type bool which is not compatible with [OCMArg any].
Even worse: You don't get a compiler warning for it unless you use BOOL instead...

Instance Message Error in Objective-C, What does this mean? And why is it happening?

I get the below error hor no apparent reason, I would try to fix it myself but I can't understand what it means. Does anyone else understand it? Here is the error and below it is the code.
Error: Receiver Type 'NSdata' for instance message does not declare a method with selector type 'EncryptAES:'
I have added a comment to the line with the error on it:
//Change the Input String to Data
NSData *objNSData = [NSData dataWithData:[Input dataUsingEncoding: NSUTF8StringEncoding]];
//Encrypt the Data
[objNSData EncryptAES:Keyword.text]; //Error appears here
NSString *InputString = [[NSString alloc] initWithData:objNSData encoding:NSUTF8StringEncoding];
What does this mean, why is it happening and what can I do to fix it?
And, what is an instance message?
The problem here is that NSData does not respond to the EncryptAES: selector. You can only invoke selectors that exist on the class. With the Objective-C runtime's loose typing, you can also invoke a selector on a class that does not respond to said selector, as long as the selector appears in the implementation of at least one other class that the compiler is working with.
All standard NSData methods can be found on the NSData Class Reference. Apple has sample code for encryption, but it is not built-in to NSData.
My guess is that you're trying to use the AESEncrypt "category" for NSData, but you don't actually have the category installed in your project.