I have a UIbutton in my app. When I click the button, it removes the last object in a NSMutableArray. For this, I want to write unit tests.Please any one give me your suggestion.
I use this code for knowing when a click on the UIButton was performed:
[viewControllerObject.backButton1 sendActionsForControlEvents:UIControlEventTouchUpInside];
Thanks,
Ricky.
At a "unit" level there are two things you're testing:
does tapping the button send the action method?
does the action method remove the last object from an array?
Ignore the first one, that's Apple's problem (or charitably it's an integration test). The second is straightforward if you think about the Assemble, Act, Assert process:
Assemble: build your view controller and its content array.
Act: call the action method.
Assert: check that the last object was removed.
-(void)testRemovalOfLastObjectOnButtonAction
{
//... build and populate the view controller
id lastObject = [array lastObject];
[viewController buttonTapped: sender];
STAssertFalse([array containsObject: lastObject], #"Object %# should be removed", lastObject);
}
Note I test explicitly whether the last object was removed, not whether the count was decremented: that could happen if any object were removed.
You can do this in several different ways. A suggestion would be to watch this great tutorial
The video explains how to unit test in UIKit.
XCode has native support for Unit Tests. If you start a new project, look for the check mark that says 'Include Unit Test'. If you use that, you will have a folder called <project_name>Tests. Open the .m file in there, and you'll see a - (void)testExample method where you can put your tests.
You can use a number of functions to test, like STAssertTrue and STAssertNotNil. Check out the Apple docs here: https://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/UnitTesting/03-Writing_Test_Case_Methods/writing_tests.html#//apple_ref/doc/uid/TP40002143-CH4-SW1
In your case, you could probably do something like this:
NSInteger arrayCount = mArray.count;
[yourInstance performButtonAction];
STAssertEquals(arrayCount -1, mArray.count);
Related
When I click a button, a UIAlertView prompts the user to type a name. This name is then created as a new 'Customer' object and inserted into a mutable array.
There is a separate mutable array called 'CustListByName', which stores a list of all names.
The problem im having is that when adding a second or third name, the app crashes. Sometimes it happens on the second try, other times on the third try. There is no information given in the debugger except for (lldb). The program reports EXC_BAD_ACCESS and then it dumps me to a screen with a bunch of assembly code.
The crash is happening in these lines of code:
Essentially, it clears the array of names and then repopulates it based upon the object array. I've studied in step by step with a breakpoint but everything seems correct up until the point of crash. It is also confusing why this happens on the second or third try, never the first.
[custListByName removeAllObjects];
for (Customer *object in custListByObject) {
[custListByName addObject:object->name];
}
Here is the code where a customer is created and inserted everytime the new customer button is clicked:
Customer *tempCust = [[Customer alloc] init];
tempCust->name =[[alertView textFieldAtIndex:0] text];
[custListByObject addObject:tempCust];
[tempCust release];
I would really appreciate help with this, thanks!
What I suspect is happening is that the UIPickerView is attempting to load a row using information from your customer array after you have already cleared it, and before you repopulate it. This would cause a bad access error.
What you may consider doing instead, is keeping two arrays, an NSMutableArray for loading the customers, and an NSArray as the actual data source for the UIPickerView. Then right before you reload the UIPickerView, you say:
dataSourceArray = [loadingArray copy];
[pickView reloadAllComponents];
Hopefully this helps.
Edit:
Here's what your updated code would look like if your loading array was called loadingCustListByName:
[loadingCustListByName removeAllObjects];
for (Customer *object in custListByObject) {
[loadingCustListByName addObject:object->name];
}
custListByName = [loadingCustListByName copy];
[pickView reloadAllComponents];
Doing this will ensure that the UIPickerView's datasource array always matches up with the number of rows it thinks it has.
This is what I have:
while(wordList){ //wordlist is instance of NSArray containing NSStrings
word.text = [wordList objectAtIndex:x]; //word is instance of UILabel
//LOOKING TO PLACE WAIT CODE HERE TO WAIT FOR "DID END ON EXIT"
input = inputBox.text; //input is instance of UITextField
[self compare:input andb:word.text]; //compare is an instance method to compare the two strings
x++;
}
I'm a beginner, if any of you could help me, that would be fantastic.
Best...
SL
Is this what you are looking for?:
[yourTextField addTarget:self
action:#selector(yourMethod:)
forControlEvents:UIControlEventEditingDidEndOnExit];
Edit:
Why don't you just break the while loop and call a separate method when the user hits next? No need to pause and trigger some other method.
The approach you're asking about won't work. If your app were to pause execution in the middle of a method, what would cause it to unpause?
The user is not going to be entering text in the middle of your loop -- that would have to have already done that before the loop starts to execute. Trigger the method that contains the loop as #Imirak suggested, or from some other user interaction, for example a button press. That way you'll know that the user has already entered the text, and the code you've written should work as expected.
One note though: the code you've shown doesn't check the return value from your compare:andb: method. You haven't really provided enough information about what you expect the method to do to do to be sure whether that makes sense or not.
Also, your loop control logic is incorrect -- as written it's either going to be an infinite loop or it will never be entered, depending on whether wordList is nil. Consider using fast enumeration syntax instead of writing a while loop, for example:
for (UILabel *currLabel in wordList)
{
// It appears as though this may be the comparison you want to do
// but there's not enough context in your question to be sure.
//
if ([inputBox.text isEqualToString:currLabel.text])
{
// Do something here. Again, it's not clear what you're trying to do.
}
}
I'm currently using a singleton as a data store for my app. I essentially store a number of events that are pulled and parsed from a web service and then added as needed. Each time I make a request from the web service, I parse the results and see if the items already exist. If they do, I delete them and add the updated version provided by the web service.
Everything appeared to be working properly until I fired up the Instruments panel to find out that my system is leaking the objects every time it loads them from the web service (from the second time on). The core method where things appear to be messing up is this one, which is located in my HollerStore singleton class:
- (void)addHoller: (Holler *)h
{
//Take a holler, check to see if one like it already exists
int i = 0;
NSArray *theHollers = [NSArray arrayWithArray:allHollers];
for( Holler *th in theHollers )
{
if( [[th hollerId]isEqualToString:[h hollerId]] )
{
NSLog(#"Removing holler at index %i", i);
[allHollers removeObjectAtIndex:i];
}
i++;
}
[allHollers addObject:h];
}
Quick explanation: I decided to copy the allHollers NSMutableArray into theHollers because it's being updated asynchronously by NSURLConnection. If I update it directly, it results in a crash. As such, I switched to this model hoping to solve the problem, however the Instruments panel is telling me that my objects are leaking. All the counts are exactly the # of items I have in my data set.
From what I can tell removeObjectAtIndex isn't effectively removing the items. Would love to get the thoughts of anybody else out there on three things:
Is my analysis correct that something else must be retaining the individual hollers being added?
Should I be using CoreData or SQLite for storing information pulled from the web service?
Do you know how long data stored in a Singleton should be available for? Until the app is killed?
Update
I think I've found the source, however perhaps someone can provide some clarity on the proper way to do this. I've created a method called parseHoller which takes a dictionary object created through SBJSON and returns my own model (Holler). Here are the last couple lines:
Holler *h = [[[Holler alloc] initFromApiResponse:hollerId
creatorId:creatorId
creatorName:creatorName
creatorImageUrl:creatorImage
comments:comments
attendees:attendees
wishes:wishes
invitees:invites
createdAt:createdAt
text:text
title:title
when:when]autorelease];
//(some other autorelease stuff is here to clean up the internal method)
return h;
I figured that since I'm returning an autoreleased object, this should be fine. Do you see anything wrong with this?
Have you tried to do a retain count on the objects that is leaking? Maybe that could clear up when or where it is being retained.
The code should be
[putObjectHere retainCount];
and then write to an NSLog
Hope it gives you something
Peter
I'm trying to implement openFlow in my project but I cant seem to get the images to show up on my uiview. What isnt clear to me is once I have the dictionary of image links, how do i tell AFOpenView that I want to use that dictionary object as my data source?
I've looked at the demo code and I see that when the flickr request finishes, he saves a copy of the dictionary results, counts them, and then tells OpenFlowView that there are x number of images, but what is never clear is how he tells OpenFlowView to use the dictionary with the results?
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary
{
// Hold onto the response dictionary.
interestingPhotosDictionary = [inResponseDictionary retain];
int numberOfImages = [[inResponseDictionary valueForKeyPath:#"photos.photo"] count];
[(AFOpenFlowView *)self.view setNumberOfImages:numberOfImages];
}
See here: http://blog.objectgraph.com/index.php/2010/04/09/how-to-add-coverflow-effect-on-your-iphone-app-openflow/
This tutorial seems to suggest that you have to call the view's setImage method multiple times, once per image.
This tells me that the implementation is confusing and weird, but for this you have to blame the component's author.
The images are loaded on demand in the 'updateCoverImage:' method of AFOpenFlowView.m
'updateCoverImage:' calls 'openFlowView:requestImageForIndex:' in AFOpenFlowViewController.m, which uses interestingPhotosDictionary.
So, it is called on demand whenever an image needs to be loaded. It wraps an operation queue so the images are loaded outside the main thread.
I am populating a UITableViewController with an NSFetchedResultsController with results creating sections that populate section headers and a section index. I am using the following method to populate the section index:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [fetchedResultsController_ sectionIndexTitles];
}
and now I've run into a problem. When I add a new element to the NSManagedObjectContext associated with the NSFetchedResultsController, the new element is saved and appropriately displayed as a cell in the UITableView ... except for one thing. If the new element creates a new SECTION, the new section index does not show up in the right hand margin unless I pop the UINavigationController's stack and reload the UITableViewController.
I have conformed to the NSFetchedResultsControllerDelegate's interface and manually invoke
[self.tableView reloadSectionIndexTitles];
at the end of both these delegate methods:
controller:didChangeSection...
controller:didChangeObject...
and while I can debug and trace the execution into the methods and see the reload call invoked, the UITableView's section index never reflects the section changes.
Again, the data shows up - new sections are physically visible (or removed) in the UITableView but the section indexes are not updated.
Am I missing something?
Looks like this is a bug we're all having. See http://iphonedevelopment.blogspot.com/2009/11/i-know-youre-tired-of-hearing-about.html for what looks to me like a fairly nasty too-many-lines-of-code solution. I went with this:
- (void)viewWillAppear:(BOOL)animated; {
// This is a dumb hack required by this bug: http://iphonedevelopment.blogspot.com/2009/11/i-know-youre-tired-of-hearing-about.html
[self.tableView reloadData];
}
It may be inefficient but unless you have reams and reams of data it probably won't do any harm. And it's only 1 line of code. So, when apple fixes their bug, you can easily take it out.
Question already 2 months old, but I ran into the same problem today. It seems like -reloadSectionIndexTitles is not working at all, so I tried a couple of potential hacks which of the following works for me:
#implementation UITableView (JKAdditions)
- (UIView *)indexView {
Class indexClass = NSClassFromString(#"UITableViewIndex");
for(UIView *subview in self.subviews){
if([subview isKindOfClass:indexClass]) return subview;
}
return nil;
}
- (void)reloadSectionIndexTitles {
UIView *indexView = [self indexView];
[indexView performSelector:#selector(setTitles:) withObject:[self.dataSource sectionIndexTitlesForTableView:self]];
[indexView setNeedsDisplay];
}
#end
I really have no idea if Apple would reject your App because of this hack, but it seems like the only option for me. Reloading the whole tableView is simply not what I want since I then have to deal with all kinds of animation problems.
I hope this helps anyone having the same problems!
To combine the accepted answer with Alex Reynolds's answer with the delay, just call reloadData with a delay that corresponds to the animation duration, so 0.4 or 0.3 seconds.
In my case, I stick the delayed method call into controller:didChangeSection:atIndex:forChangeType: (it's a Core Data app).
The result, when a section is added or deleted, is the standard animation of the cell, followed by the index being updated when the data is reloaded.
It's ugly and makes me cringe, but I am okay with the result. I also submitted a bug to Apple, #8589547.
Try putting it at the end of -controllerDidChangeContent:, somewhere after [self.tableView endUpdates].
Another thing I do (that works for me, can't guarantee it will work for you) is perform a selector after a very short delay, e.g.:
[self performSelector:(#selector(refreshSectionIndex)) withObject:nil afterDelay:0.2];
// ...
- (void) refreshSectionIndex {
[self.tableView reloadSectionIndexTitles];
}
Core Data and NSFetchedResultsController in particular seem buggy as hell, where delegate table view updates get out of sync with the fetched data, causing the application to crash. I really hope Apple is taking steps to fix the bugs in these frameworks in the 4.0 SDK. It's pretty frustrating.