Unit Testing in Xcode, does it run the app?

后端 未结 10 956
陌清茗
陌清茗 2020-11-29 21:54

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,

相关标签:
10条回答
  • 2020-11-29 21:59

    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
    
    0 讨论(0)
  • 2020-11-29 21:59

    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.

    0 讨论(0)
  • 2020-11-29 22:00

    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.

    0 讨论(0)
  • 2020-11-29 22:01

    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;
    }
    
    0 讨论(0)
  • 2020-11-29 22:03

    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))
    }
    
    0 讨论(0)
  • 2020-11-29 22:04

    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.

    0 讨论(0)
提交回复
热议问题