Testing a module in Xcode, does this application launch?

I got confused by a strange problem that I have not encountered before.

When you execute cmd + U to run unit tests (e.g. OCUnit), does it really call main.m, the new appDelegate and launch the application, as if you had pressed cmd + R?

I am only asking because I am using CoreData for this DataLayer. I mock the DataLayer in my tests, but as soon as I apply the getAll method, which actually calls CoreData, the / xcode application throws an exception from the managed object model, it cannot be nil. Which I understand, but I do not actually mean the new DataLayer class, and I set a breakpoint in my loadView method mainviewcontroller, where it calls the DataLayer getAll method. It doesn't matter in the tests, because it's a layout, but it seems to be calling the real instance.

So, back to my question, when pressing cmd + U does it also launch the application and then run the tests?

+43
ios iphone unit-testing xcode
Mar 30 '13 at 3:38
source share
9 answers

The application is actually running, but there is a trick you can use to prevent it from starting.

int main(int argc, char* argv[]) { int returnValue; @autoreleasepool { BOOL inTests = (NSClassFromString(@"SenTestCase") != nil || NSClassFromString(@"XCTest") != nil); if (inTests) { //use a special empty delegate when we are inside the tests returnValue = UIApplicationMain(argc, argv, nil, @"TestsAppDelegate"); } else { //use the normal delegate returnValue = UIApplicationMain(argc, argv, nil, @"AppDelegate"); } } return returnValue; } 
+60
Mar 31 '13 at 1:15
source share

Here's a Sulthan answer option that uses XCTest, which is standard for test classes generated by Xcode 5.

 int main(int argc, char * argv[]) { @autoreleasepool { BOOL runningTests = NSClassFromString(@"XCTestCase") != nil; if(!runningTests) { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } else { return UIApplicationMain(argc, argv, nil, @"TestAppDelegate"); } } } 

This is included in main.m, which should be supported by files in the standard layout of the project.

Then in your test directory add:

TestAppDelegate.h

 #import <Foundation/Foundation.h> @interface TestAppDelegate : NSObject<UIApplicationDelegate> @end 

TestAppDelegate.m

 #import "TestAppDelegate.h" @implementation TestAppDelegate @end 
+19
Dec 14 '13 at 20:42 on
source share

In Swift, I prefer to bypass the normal execution path inside application: didFinishLaunchingWithOptions :

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { guard normalExecutionPath() else { window = nil return false } // regular setup return true } private func normalExecutionPath() -> Bool { return NSClassFromString("XCTestCase") == nil } 

The code inside the guard will remove all views created from the storyboard.

+6
Dec 20 '15 at 17:19
source share

If you use Swift (you probably don't have main.c ), you need to follow these steps:

1: remove @UIApplicationMain in AppDelegate.swift

2: Create an empty TestingAppDelegate.swift

 import UIKit class TestingAppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? } 

3: Create a file called main.swift :

 import Foundation import UIKit let isRunningTests = NSClassFromString("XCTestCase") != nil if isRunningTests { UIApplicationMain(C_ARGC, C_ARGV, nil, NSStringFromClass(TestingAppDelegate)) } else { UIApplicationMain(C_ARGC, C_ARGV, nil, NSStringFromClass(AppDelegate)) } 
+5
Jun 26 '15 at 10:38
source share

Yes, your target will have a target dependency on the target of the application, so the goal of the application will be built by pressing Cmd + U or Cmd + Shift + U.

+1
Mar 30 '13 at 20:23
source share

I found another solution to the problem:

 int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, ({ ![NSProcessInfo processInfo].environment[@"XCTestConfigurationFilePath"] ? @"AppDelegate" : nil; })); } } 

From here: http://qualitycoding.org/app-delegate-for-tests/#comment-63984

+1
Apr 21 '16 at 7:41
source share

I use the Tomasz Bak approach plus some dwb response code and come up with the following:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { BOOL runningTests = NSClassFromString(@"XCTestCase") != nil; if (runningTests) { self.window.rootViewController = [UIViewController new]; return true; } // Your normal code below this .... } 
0
Jan 26 '16 at 10:52
source share

Using xCode 7 and xCtool

xctool is able to run unit tests without running the application.

To do this,

1. Update target settings to run without a host application.

Select your project β†’ then check the target β†’ Set the host application to none.

enter image description here

2. Install xctool if you do not have it.

 brew install xctool 

3. Run the tests using the terminal using xctool.

 xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator 
0
May 25 '16 at 9:40
source share

The excellent answers above that involve dynamically changing the application delegate at runtime.

The little modification I'm doing is to detect unit test run by querying NSProcessInfo . The advantage is that you do not need to have a class that can be discovered to check if unit tests are running.

  int main(int argc, char * argv[]) { // Put your App delegate class here. const Class appDelegateClass = [ATAppDelegate class]; NSDictionary *const environmentDictionary = [[NSProcessInfo processInfo] environment]; const BOOL runningUnitTests = environmentDictionary[@"XCInjectBundleInto"] != nil; NSString *delegateName = runningUnitTests ? nil : NSStringFromClass(appDelegateClass); @autoreleasepool { return UIApplicationMain(argc, argv, nil, delegateName); } } 

The @"XCInjectBundleInto" property in environmentDictionary is the path to your batch testing and is configured for Xcode.

0
Feb 06 '17 at 10:07 on
source share



All Articles