Short version of the question:
What is wrong with the following Kiwi/iOS mock expectation?
[[mockDelegate should] receive:#selector(connectionDidSucceedWithText:andStatus:) withArguments:[testString1 stringByAppendingString:testString2],theValue(value),nil];
Long version of question:
I am trying to write a test in Kiwi, iOS for a simple class that handles a NSConnection. To test that the class handles the callback from the NSConnection I send it the delegate methods NSConnection normally does. I have a delegate in the class that sends data back to whoever uses my class. To test my class I have to inject a mocked delegate and then check that my desired methods are called. Simple as that :)
My code for the Kiwi test is:
//Some ivars declared elsewhere:
testString1 = #"asd323/4 d14";
testString2 = #"as98 /2y9h3fdd14";
testData1 = [testString1 dataUsingEncoding:NSUTF8StringEncoding];
testData2 = [testString2 dataUsingEncoding:NSUTF8StringEncoding];
mockURLRespons = [NSHTTPURLResponse mock];
int value = 11111;
id mockDelegate = [KWMock mockForProtocol:#protocol(SharepointConnectionDelegate)];
communicator = [[SharepointCommunicator alloc] init];
it (#"should send recieve data back to delegate2", ^{
[communicator setDelegate:mockDelegate];
[mockURLRespons stub:#selector(statusCode) andReturn:theValue(value)];
[(id)communicator connection:niceMockConnector didReceiveResponse:mockURLRespons];
[(id)communicator connection:niceMockConnector didReceiveData:testData1];
[(id)communicator connection:niceMockConnector didReceiveData:testData2];
[(id)communicator connectionDidFinishLoading:niceMockConnector];
[[mockDelegate should] receive:#selector(connectionDidSucceedWithText:andStatus:) withArguments:[testString1 stringByAppendingString:testString2],theValue(value),nil];
});
And in my SharepointCommunicator.m:
-(void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response {
if (connection != aConnection) {
[connection cancel];
connection = aConnection;
}
responseData = [[NSMutableData alloc] init];
statusCode = [(NSHTTPURLResponse*)response statusCode];
}
-(void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data {
if (aConnection != self.connection)
return;
[responseData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *txt = [[NSString alloc] initWithData:responseData encoding: NSASCIIStringEncoding];
NSLog(#"Statuscode: %i", statusCode);
NSLog(#"Data is: %#",txt);
[delegate connectionDidSucceedWithText:txt andStatus:statusCode];
[self.connection cancel];
self.connection = nil;
}
This code works and is correct. Debugging it with checkpoint shows it does as expected. The values of statusCode is 11111. and txt is testString1+textString2. Still it fails on the last row on in the test with the following error:
error: -[kiwiSharepointCommunicatorTest Sharepointcommunicator_AStateTheComponentIsIn_ShouldSendRecieveDataBackToDelegate2] : 'Sharepointcommunicator, a state the component is in, should send recieve data back to delegate2' [FAILED], mock received unexpected message -connectionDidSucceedWithText:"asd323/4 d14as98 /2y9h3fdd14" andStatus:11111
Test Case '-[kiwiSharepointCommunicatorTest Sharepointcommunicator_AStateTheComponentIsIn_ShouldSendRecieveDataBackToDelegate2]' failed (3.684 seconds).
Removing the last row in the test still generate the same error. I guess my understanding of receive:withArguments: is wrong..
You have to call [[mockDelegate should] receive... before the call to connectionDidFinishLoading to prepare the mockDelegate for the message it's about to receive.
Related
I am trying to retrieve a Facebook profile picture, however I am having trouble being able to check when the image has been downloaded?
First I create a variable.
#property (strong, nonatomic) NSMutableData *imageData;
Than I start the connection.
-(void)getUserPicture {
//Grab user profile picture
imageData = [[NSMutableData alloc] init]; // the image will be loaded in here
NSString *urlString = [NSString stringWithFormat:#"http://graph.facebook.com/%#/picture?type=large", userId];
NSMutableURLRequest *urlRequest =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest
delegate:self];
if (!urlConnection) NSLog(#"Failed to download picture");
}
After that I try to check when it is done so I can upload the file to my backend, however my problem is connectionDidFinishLoading calls almost instantly before the image has downloaded.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
imageData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[imageData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
userPicture = [UIImage imageWithData:imageData];
NSLog(#"%#",userPicture); //this returns null :(
}
The weird thing is if I call this method twice, the NSLog doesn't return null, it actually returns the photo. So why is connectionDidFinishedLoading calling before the image has downloaded from Facebook?
The problem is almost certainly neither NSURLConnection nor the Facebook API, but rather how you're calling it. But, your question doesn't include enough information for us to diagnose it.
So, first, expand your methods to include more diagnostic information, for example:
// check the response header when we receive the response
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
imageData = [NSMutableData data];
// if `statusCode` is not 200, show us what it was ...
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
int statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
NSLog(#"Status code was %ld, but should be 200.", (long)statusCode);
NSLog(#"response = %#", response);
}
}
}
// make sure to detect and report any errors
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError: %#", error);
}
// when you're done, if we fail to create `UIImage`, then it obviously
// wasn't an image, so let's see what it was.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
userPicture = [UIImage imageWithData:imageData];
// if not an image, then ...
if (!userPicture) {
NSString *responseString = [[NSString alloc] initWithData:imageData encoding:NSUTF8StringEncoding];
if (responseString) {
// if it was a string, show it to us ...
NSLog(#"did not receive image: responseString = %#", responseString);
} else {
// if neither a string nor image data, then what was it ...
NSLog(#"did not receive image: imageData = %#", imageData);
}
}
// By the way, I'd expect to see you do something here to update the UI with the image;
// all of these delegate methods were called asynchronously, so you have
// to do something here that triggers the update of the UI, e.g.
//
// self.imageView.image = userPicture
}
By the way, I typed the above without the benefit of Xcode's syntax checking and the like, so don't be surprised if there are some errors there. But worry less about the actual code and focus on the the three diagnostic pillars this illustrates: 1. Look at the response headers and make sure they're ok, not reporting some non-200 status code; 2. Implement delegate that will report networking errors; and 3. If image conversion failed, then you obviously didn't receive an image, so stop and figure out what you actually received. (Often if the server had trouble fulfilling your request, the response is actually HTML or something like that which tells you why it had problems. If you don't look at it, you're flying blind.)
Second, you can watch the network connection by using Charles (or something like that). Run the app on the simulator and then watch the network connection as the app runs.
Third, if you're still having problems, create a MCVE. Namely, we don't want to see all of your code, but you should instead create the simplest possible example that manifests the problem you describe. Don't ask us to pour through tons of code, but rather make it as absolutely bare-bones as possible.
So I'm not sure why connectionDidFinishLoading is getting called instantly after you set the connection, but I may be able to help you work around the issue.
Try this:
-(UIImage *) getImageFromURL:(NSString *)fileURL {
UIImage * result;
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
result = [UIImage imageWithData:data];
return result;
}
Where fileURL is the a string with the url.
If you want to perform an action after the request is sent try this instead:
-(UIImage *) getImageFromURL:(NSString *)fileURL {
UIImage * result;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
result = [UIImage imageWithData:data];
return result;
dispatch_async(dispatch_get_main_queue(), ^{
//code for operation after download
});
});
}
Let me know how it goes
This is first time I am trying.
Till now I am successful in getting DATA from SERVER in JSON format.
Now what I want to do is,
I have two NSString values that I have to send to server and server will check for them.
I don't know what checking mechanism is behind.
I am just sending two strings and server will return me try or false.
And I have to show that true or false thing.
All this will be called onClick of UIButton
Here what I tried,
NSString *str = [NSString stringWithFormat:#"http://XXXXXXXXXXXXXXXXXXXXXXX/api/CaptchaImage/CheckCaptchValid?validstring={%#}&encodestring={%#}",string1,string2];
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init];
[req setURL:[NSURL URLWithString:str]];
NSURLConnection *connGet = [[NSURLConnection alloc]initWithRequest:req delegate:self];
if(connGet)
{
NSLog(#"Connected successfully");
}
else
{
NSLog(#"Not connected successfully");
}
It gives me NSLog as connected successfully,
But I am struck here,
I want a response from server too in NSString format, either True or False.
Can any one guide me for further steps.
I tried some SO links, but didn't get much.
Thanks in advance.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
-(void)connectionDidFinishLoading: (NSURLConnection *)connection {
response = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
}
Try this, but I have not tested this code. add appropriate delegate method.
Hope this will work for you
I'm trying to get data from a website to display it inside a table view
My code:
-(void)loadTutorials {
NSURL *url = [NSURL URLWithString:[#"http://www.example.com/search?q=" stringByAppendingString:self.searchString]];
NSURLRequest *UrlString = [[NSURLRequest alloc] initWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:UrlString
delegate:self];
[connection start];
NSLog(#"Started");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
TFHpple *tutorialsParser = [TFHpple hppleWithHTMLData:data];
NSString *tutorialsXpathQueryString = #"//div[#id='header']/div[#class='window']/div[#class='item']/div[#class='title']/a";
NSArray *tutorialsNodes = [tutorialsParser searchWithXPathQuery:tutorialsXpathQueryString];
NSMutableArray *newTutorials = [[NSMutableArray alloc] init];
for (TFHppleElement *element in tutorialsNodes) {
Data *tutorial = [[Data alloc] initWithTitle: [[element firstChild] content]
Url: [#"http://www.example.com" stringByAppendingString: [element objectForKey:#"href"]]
];
[newTutorials addObject:tutorial];
}
_objects = newTutorials;
[self.tableView reloadData];
}
but the data is not showing up, is the data not finished loading?
I got it to working without NSURLConnection but then it will stop the program until the data is recieved
According to NSURLConnectionDataDelegate
connection:didReceiveData:
is called in a incrementally manner.
The newly available data. The delegate should concatenate the contents
of each data object delivered to build up the complete data for a URL
load.
So this means you should append new data within this method.
Then, in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
you should manipulate your data.
So, for example
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// Create space for containing incoming data
// This method may be called more than once if you're getting a multi-part mime
// message and will be called once there's enough date to create the response object
// Hence do a check if _responseData already there
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data
[_responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Parse the stuff in your instance variable now
}
Obviously you should also implement the delegate responsible for error handling.
A simple note is the following. If data is too big and you need to do some computations stuff (e.g. parsing), you could block the UI. So, you could move the parsing in a different thread (GCD is your friend). Then when finished you could reload the table in the main thread.
Take a look also here for further info: NSURLConnectionDataDelegate order of functions.
My app goes to a viewcontroller, makes two automatic server requests, makes the connection, retrieves the data and correctly displays it, and is done. The user clicks a "likes" button and two more server requests are made - successfully. Displays are correct. Should be done. Then it crashes, with the error:
[__NSCFNumber isEqualToString:]: unrecognized selector sent to instance
I'm using the very handy SimplePost class (by Nicolas Goles). Here are my requests, which are both called in viewDidLoad:
- (void) setScore {
Profile *newPf = [[Profile alloc] initID:thisUser profil:#"na" scor:score];
NSMutableURLRequest *reqPost = [SimplePost urlencodedRequestWithURL:[NSURL URLWithString:kMyProfileURL] andDataDictionary:[newPf toDictPf]];
(void) [[NSURLConnection alloc] initWithRequest:reqPost delegate:self];
}
- (void) saveHist {
History *newH = [[History alloc] initHistID:thisUser hQid:thisQstn hPts:score hLiked:NO];
NSMutableURLRequest *reqHpost = [SimplePost urlencodedRequestWithURL:[NSURL URLWithString:kMyHistURL] andDataDictionary:[newH toDictH]];
(void) [[NSURLConnection alloc] initWithRequest:reqHpost delegate:self];
}
The only "new" thing with my custom classes (Profile and History) is the BOOL for hLiked, but it's "working" - the database is updating correctly.
Then, the user can click a "Likes" button (+ or -). Here are the other requests:
- (IBAction)likeClick:(id)sender {
double stepperValue = _likeStepper.value;
_likesLbl.text = [NSString stringWithFormat:#"%.f", stepperValue];
[self updateLikes];
[self updateHist];
}
- (void) updateLikes {
// update the question with the new "liked" score
NSInteger likesN = [_likesLbl.text integerValue];
Questn *qInfo = [[Questn alloc] initQwID:thisQstn askID:0 wCat:#"na" wSit:#"na" wAns1:#"na" wPts1:0 wAns2:#"na" wPts2:0 wAns3:#"na" wPts3:0 wAns4:#"na" wPts4:0 wJust:#"na" wLikes:likesN ];
NSMutableURLRequest *reqPost = [SimplePost urlencodedRequestWithURL:[NSURL URLWithString:kLikesURL] andDataDictionary:[qInfo toDictQ]];
(void) [[NSURLConnection alloc] initWithRequest:reqPost delegate:self];
}
- (void) updateHist {
History *newH = [[History alloc] initHistID:thisUser hQid:thisQstn hPts:98989 hLiked:YES];
NSMutableURLRequest *reqHpost = [SimplePost urlencodedRequestWithURL:[NSURL URLWithString:kHistURL] andDataDictionary:[newH toDictH]];
(void) [[NSURLConnection alloc] initWithRequest:reqHpost delegate:self];
}
Messy, right? Here's my connection code:
// connection to URL finished with Plist-formatted user data array returned from PHP
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSDictionary *array = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:0 errorDescription:nil];
BOOL keyLikeExists = [array objectForKey:#"likes"] != nil;
if( keyLikeExists ) {
_likesLbl.text = [array objectForKey:#"likes"];
}
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Connection did fail." );
}
It all does a good job, and then a couple of seconds later it crashes with that "unrecognized selector" error mentioned above, like there's still some URL activity happening. There shouldn't be.
Anybody seen this kind of thing before? Many thanks for any help!
Somewhere in your code there's a call to the method isEqualToString:. The thing that's being sent that message is a NSNumber object rather than a string. Either there's a logic problem concerning the object type or there's a memory problem where a string was over-released and its memory is being re-used to hold a number.
Without seeing the context for the call, it's hard to guess.
If you break on the exception, the stack trace should tell you where in the code it's failing.
In my iPAD application, I have 6 UITableViews. To get data for each of the tableview, I call a Webservice using NSURLConnection and parse the xml I get back from the Webservice and store data into the database.
Since I have 6 UITableView, I send the Webservice request for each of the views at the same time. However, the problem that I am facing is that, for my app to receive data from the Webservice on the -(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data for 1 table view keeps depending upon the database operations performed by the parsers of the other tableviews.
For example, the webservice request for tableview's A, B, C, D are all sent at the same time. if I get back the data on the -(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data function, until the xml received is parsed and saved to my database, I am not getting the response back for the other tableviews.
I am unable to figure out what I am doing wrong. I know NSURLConnection is asynchronous but the response I am getting does not seem so.
Here is my code -
For sending the Webservice request -
- (void) callMedicationWebService
{
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [[NSMutableData data] retain];
}
}
-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response
{
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection
didReceiveData:(NSData *) data
{
[webData appendData:data];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"HH:mm:ss"];
NSString *alertMessage = [formatter stringFromDate:[NSDate date]];
[formatter release];
NSLog(#"got data back from WS %#", alertMessage);
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
[connection release];
// Parse xml
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:[CommonHelper decodeHTMLCharactorsFromString:webData]];
TableAHandler *handler = [[TableAHandler alloc] init];
[handler initTableAHandler];
[xmlParser setDelegate:handler];
[xmlParser setShouldResolveExternalEntities:YES];
[xmlParser setShouldProcessNamespaces:YES];
BOOL success = [xmlParser parse];
}
Would someone be able to help me what I am doing wrong?
Asynchronous doesn't necessarily mean that the callback function itself is called in a separate thread.
if you want all the parsing processes to happen at the same time you're gonna have to move the parsing processes to separate threads.
although the better solution would be not to use 5 different URLRequests, and to use only one that returns all the required information.