Storyboard UI testing from code - ios

I have an iOS app and I want to test its UI from code (with unit-test). I want to imitate a user's activity and storyboard activity: tap on button, tap on barbutton, write to textfield, perform segue.
I have problem with seques. I have a TableViewConrtoller in a NavigationController. Firstly, how can i reach the TableViewController from code?
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
UINavigationController *nav = [storyboard instantiateInitialViewController];
GoodHabitsViewController *good = [nav.viewControllers objectAtIndex:0];
NSLog(#"%#", good.addButton.title);
[good performSegueWithIdentifier:#"AddGoodHabitSegue" sender:good];
NSLog write correct value, the button's title is "add", and "add" appears in log.
But after the performSequeWithIdentifier the following warning appears:
Warning: Attempt to present <AddGoodHabitViewController: 0x74c8310> on <UINavigationController: 0x764e120> whose view is not in the window hierarchy!
How can I perform the seque, how can I push addButton (UIBarButton) and how can I make reference to AddGoodHabitViewController (where the segue point).

There are two varieties of tests that can be run on the OCUnit platform - so called "application" tests, and so-called "logic" tests.
Only "application" tests allow you to exercise UIViewControllers, UIViews and so forth. If you specified "create test target" when you created the project, it will use the "application" style. Otherwise adding a teset target later, by default, uses the "logic" style. To covert an existing test target to use "application" tests, TwoBit Labs have a nice: guide
Another way to exercise code all the way up to the view controller and views is to use the Cedar BDD test framework. Cedar tests are run inside of an iOS application, so besides being able to test ViewControllers and Views, its also possible to test on either device or simulator.
UIAutomation allows the execution of automated functional tests, by driving the UI itself (as opposed to exercising UI code). The problem I have with UIAutomation is that, as far as I know, its not possible to execute it from the cmd-line, making it hard to include in an automated test suite - one that would be run by a continuous integration server. . . . someone might come up with a work-around for this using Automator.app or similar, but so far nobody has.
Calabash is another great UI testing framework, and can be run from the cmd-line, so doesn't suffer from the limitations I described above, wrt UIAutomation.
Bear in mind that automated functional testing, and testing UIViewControllers and Views at the code-level are two very different things. The latter certainly has value, and simply requires setting up the bundle loaders correctly.
Update: With recent versions of Xcode 5+, application-style tests are the default.

As the error message points out, the problem is that you're trying to present on a UIViewController whose view is not in the UIWindow hierarchy.
The easiest way to fix it:
- (void)testExample {
//
// Arrange
// Storyboard
//
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
//
// Arrange
// View Controller
//
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
[UIApplication sharedApplication].keyWindow.rootViewController = viewController;
//
// Act
//
[viewController performSegueWithIdentifier:#"ModalSegue" sender:nil];
//
// Assert
//
XCTAssertEqualObjects(viewController.presentedViewController.title, #"Second");
}

Unit tests (i.e. those you get when adding unit tests to a project via the Xcode template) are not for testing your UI. You need to look at UI Automation which allows you to code up UI actions using JavaScript.

Check out KIF. It sounds like pretty much what you need:
https://github.com/kif-framework/KIF
It's an Objective-C based UI Testing framework that integrates with the Accessibility framework built in to the app (as UI Automation does).
Or follow this tutorial to ensure it is what you want:
http://www.raywenderlich.com/61419/ios-ui-testing-with-kif
Hope you find your solution!

Related

Cordova plugin, with native storyboard UI

I am in the midst of creating a Cordova camera plugin. I know there are many alternatives out there, but none of them have all of the functionality that I am looking for, or they are outdated.
So I have been working with the AVCam-iOSUsingAVFoundationtoCaptureImagesandMovies project that Apple provides to get you started with their AVFoundation library.
After working with the library a bit, I realized that I wanted to make it into a Cordova plugin, but the only thing that I am concerned about is the UI functionality of the plugin. So I am wondering if it is possible to include the storyboard files (that render the view / buttons) in the Plugin, so that they work seamlessly, or if it needs to be programmatically created, like other Camera plugins (like CameraPreview) do.
Yeah, you can add a storyboard to a plugin.
First add the storyboard in the plugin.xml
<resource-file src="src/ios/YourStoryboard.storyboard"/>
Then in the code you can get the initial view controller like this:
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"storyboardName" bundle:nil];
UIViewController * vc = [sb instantiateInitialViewController];

Xcode 7 navigationcontrller issue

I am facing some kind of issue in my when I compile my code with xcode 7.
Here is my code:
UIViewController *vcSomeObj = [self.storyboard instantiateViewControllerWithIdentifier:#"vcSOmeClass"];
vcSomeObj.channelID = detailOfUserTable.ID;
[self.navigationController setViewControllers:#[vcSomeObj] animated:NO];
This is working fine when I compile and run my code from xcode6.4.
This thing hang my application. And when I goes to any app and come agian to my app it will take to main controller and after sometime app crashes.
I can't find anything in debug.
In storyboard file We need to select English check box also otherwise application will hang an crash.
In my opinion.you set the current navigation's viewcontroller.what's happen to current viewController in navigation stack when you in current viewController,so ,I guess you can do like this
[self.navigationController setViewControllers:#[vcSomeObj,self] animated:NO];

What's causing the massive changes in autorotation in 8.1 compared to 8.0.2?

I'm coding a video processing app and was just about to submit it to the app store when ios 8.1 came out. I updated my iPhone as well as XCode and all hell broke loose. In my simple single viewcontroller interface nothing is rotating anymore except for the statusbar, which also doesn't get automatically hidden anymore in landscape mode...
I figured it was because I was using the deprecated willAnimateRotationToInterfaceOrientation: for what little custom rotation actions I had, so I implemented traitCollectionDidChange: and viewWillTransitionToSize: to specs instead. However viewWillTransitionToSize never gets called in my app and traitCollectionDidChange: is only called once, at startup. The device simply isn't telling the app that the device has rotated.
After googling I've also tried using name:UIDeviceOrientationDidChangeNotification. At least my selector does get called for that one but I don't know how to manually handle all rotation.
My didFinishLaunching... and viewDidLoad are very simple. alloc UIWindow, storyboard, set my viewcontroller from there, make it rootviewcontroller, makekeyandvisible. All based on one of Apple's AVFoundation demo apps.
Then in didload I add some subviews and a toolbar etc, nothing out of the ordinary and obviously it did work on 8.0 and 8.0.2 on all kinds of devices as well as the 7.1 simulator etc. Still runs flawlessly on my iPad with 8.0.2... Reason I haven't posted any code is I'm 100% sure everything is correct on that end.
Main weird thing is I can't seem to find anyone with this problem.
No errors in console or elsewhere either.
Does anyone have any idea of what might be causing this? I didn't think a point release would make such massive differences and again, no one else seems to be having this. Could it be an issue/bug in the actual storyboard file?
And, mainly, since I can get rotation notifications through UIDeviceOrientationDidChangeNotification, how do I manually handle all rotation/resizing stuff? I have been looking all over for answers but to no avail and am out of time to spend on this project currently :(
Cheers!
alloc'ing UIWindow will be the problem.
First, Make sure your navigation controller (or whatever you're using) is set as "Initial View Controller" in your storyboard.
Secondly, in your AppDelegate.m file, remove any references to UIWindow and rootViewController that appear in application didFinishLaunchingWithOptions. In my case, removing the following two lines fixed my issues.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window makeKeyAndVisible];
You also don't need to set the window's rootViewController if using storyboards.
They're simply not needed when using storyboards, but until 8.1 there was never any harm using them. It took my 2 days to figure this out, so hopefully it will help you and others too.

STORYBOARD EXCEPTION - Storyboard doesn't contain a view controller with identifier

I am currently developing an iOS application for both iPhone and iPad. I'm trying to show a NavigationController to make a module that shows files. In an iPhone it works just fine, but I can´t get it to work in iPad.
I'm getting the following error:
'Storyboard (<UIStoryboard: 0x919b200>) doesn't contain a view controller with identifier 'FilesNavigation''
and this is the code:
case 10: // Files
{
UINavigationController *navigationController = [storyboard instantiateViewControllerWithIdentifier:#"FilesNavigation"];
FilesViewController *filesViewController = [storyboard instantiateViewControllerWithIdentifier:#"Files"];
[navigationController pushViewController:filesViewController animated:YES];
self.slidingViewController.topViewController = navigationController;
break;
}
And in my storyboard I have already set the Storyboard ID for the navigation controller.
>##Custom Class
>Class: UINavigationController
>##Identity
>Storyboard ID: FilesNavigation
I used this code for another case on the switch/case block for other module and it works for iPad too, its the first time that I have this error.
I figured it out, the problem was that the Storyboard wasn't updating in the device.
Uninstall the app from the simulator/iPhone/iPad
Product > Clean
Build & Run
As stated in #T0m_Twt's answer in this question
In the right pane, in the identity inspector section give a storyboard Id to your Navigation Controller.
I took a screenshot for you
Here's another case:
There are two localized storyboards. Only one of them has the view controller you want.
By chance, do you have a storyboard for iPhone and a different one for iPad? If you do, then the iPad one might not have a storyboard with that identifier. Also when running on an iPad, set a break point in your code and verify what your "storyboard" object is.
I use a category on UIStoryboard that will give me the storyboards for each platform.
I can use [UIStoryboard storyboardHome] and that will determine if it needs iPad or iPhone specific and return that to me. I also ensure my storyboards follow proper naming. So I would have Home_iPad and Home_iPhone.
TL;DR - clear everything and remove old version of the app as well, if there's any.
It may sound crazy, but suppose that if you have changed target name which changed bundle name as well, you might have both the old and new version of the app installed in the simulator / device, and this might cause trouble for the UIStoryboard instance failed to instantiate some view controllers, which might eventually cause the trouble described in the question. To solve it, you need to remove all related apps or simply reset the simulator / device.

Generating a view programmatically in iOS

Update:
Thanks for all the tips, everyone. The tutorial mentions that a XIB file is provided (which I don't have) so I'm doing something wrong in how I'm creating the the project.
I am following Apple's Core Data Tutorial for iOS. This tutorial has not been updated for ARC—apparently for Xcode 4, since it asks to "create a new project using the Window-Based Application template in the iOS section."
Since that option doesn't exist under Xcode 4.4.1, I looked around Stack Overflow and read that I should create an empty application. As per the tutorial instructions, I created no Storyboard or NIB file.
Other than updating the code for ARC (using strong in place of retain and not implementing the provided dealloc method), I'm confident that the code in my project matches that of the tutorial up to the end of the chapter "The Table View Controller." At this point, the tutorial says I should be able to run the project and get a view.
Instead, I get a blank, black screen.
Maybe my problem is too vague to solve here, but should I perhaps be using a different project template? Which one?
I have only two classes: a RootViewController and an AppDelegate. AppDelegate imports RootViewController and contains a UIWindow property. Again, there is no Storyboard or NIB in the project.
I can provide any code too if there's someplace specific to look.
If you want to check if your setup is correct do the following:
add a background color to your window
self.window.backgroundColor = [UIColor whiteColor];
make sure you tell the window to display itself
[self.window makeKeyAndVisible];
make sure your view controller is the window's rootViewController
self.window.rootViewController = myViewControllerInstance;
Choose Single View Application, and uncheck 'Use storyboards" field. The rest should go the same.

Resources