I\'m running into a strange problem that I haven\'t run into before.
When you do cmd+U to run your Unit Tests (OCUnit for example) does it actually call the main.m,
Here's a variation of Sulthan's answer that uses XCTest, which is the default 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 goes into main.m, which should be under Supporting Files in a standard project layout.
Then in your tests directory add:
TestAppDelegate.h
#import <Foundation/Foundation.h>
@interface TestAppDelegate : NSObject<UIApplicationDelegate>
@end
TestAppDelegate.m
#import "TestAppDelegate.h"
@implementation TestAppDelegate
@end
In Swift, I prefere to bypass a 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
}
Code inside guard
will remove any views created from storyboard.
Yes, your test target will have a target dependency to the app target, so the app target will be built when you press Cmd+U or Cmd+Shift+U.
The application is actually run but there is a trick you can use to prevent it from running.
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;
}
If you're using Swift (you probably don't have a main.c
), you have to do 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))
}
Excellent answers above that suggest dynamically changing the application delegate at run time.
The small modification I make is to detect a unit test run by querying NSProcessInfo
. The advantage is that you don't need to have a class that can be detected to see 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 unit tests bundle and is set up by Xcode.