Xcode 中的单元测试,它是否运行应用程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15714697/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 03:05:50  来源:igfitidea点击:

Unit Testing in Xcode, does it run the app?

iphoneiosxcodeunit-testing

提问by Mark W

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, new up the appDelegate and run the app as if your had pressed cmd+R?

当您执行 cmd+U 来运行单元测试(例如 OCUnit)时,它是否真的调用了 main.m、新建了 appDelegate 并运行应用程序,就像您按下了 cmd+R 一样?

I only ask because I'm using CoreData behind this DataLayer. I'm mocking out the DataLayer successfully in my tests, but once I implemented a getAll method that is actually calling CoreData, the app/xcode is throwing an exception about the managed object model can't be nil. Which I understand, but I'm not meaning to actually new up the DataLayer class, and I've put a break point in my mainviewcontroller loadView method where it is calling the DataLayer getAll method. It shouldn't matter with tests because this is a mock object, but it's apparently calling the real instance.

我之所以这么问是因为我在这个 DataLayer 后面使用了 CoreData。我在我的测试中成功模拟了 DataLayer,但是一旦我实现了一个实际调用 CoreData 的 getAll 方法,应用程序/xcode 就会抛出一个关于托管对象模型的异常不能为零。我明白,但我并不打算真正更新 DataLayer 类,而且我已经在调用 DataLayer getAll 方法的 mainviewcontroller loadView 方法中放置了一个断点。测试应该无关紧要,因为这是一个模拟对象,但它显然是在调用真实实例。

So back to my question, when pressing cmd+U does it also run the app first then run the tests?

所以回到我的问题,当按下 cmd+U 时,它是否也先运行应用程序然后运行测试?

回答by Sulthan

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;
}

回答by dwb

Here's a variation of Sulthan's answer that uses XCTest, which is the default for test classes generated by XCode 5.

这是使用 XCTest 的 Sulthan 答案的变体,这是 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 Filesin a standard project layout.

这进入 main.m,它应该在标准项目布局中的支持文件下。

Then in your tests directory add:

然后在您的测试目录中添加:

TestAppDelegate.h

TestAppDelegate.h


#import <Foundation/Foundation.h>

@interface TestAppDelegate : NSObject<UIApplicationDelegate>
@end

TestAppDelegate.m

TestAppDelegate.m


#import "TestAppDelegate.h"

@implementation TestAppDelegate
@end

回答by Tomasz B?k

In Swift, I prefere to bypass a normal execution path inside application: didFinishLaunchingWithOptions:

在 Swift 中,我更喜欢绕过内部的正常执行路径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 guardwill remove any views created from storyboard.

里面的代码guard将删除从故事板创建的任何视图。

回答by Rémy Virin

If you're using Swift (you probably don't have a main.c), you have to do these steps :

如果您使用 Swift(您可能没有main.c),则必须执行以下步骤:

1: remove @UIApplicationMainin AppDelegate.swift

1:删除@UIApplicationMainAppDelegate.swift

2: Create an empty TestingAppDelegate.swift

2:创建一个空的 TestingAppDelegate.swift

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

3: Create a file called main.swift:

3:创建一个名为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))
}

回答by nepo

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

从这里:http: //qualitycoding.org/app-delegate-for-tests/#comment-63984

回答by rustylepord

Using xCode 7 and xCtool

使用 xCode 7 和 xCtool

xctoolis capable of executing unit tests without running the app.

xctool能够在不运行应用程序的情况下执行单元测试。

To get this working,

为了让这个工作,

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

1 . 更新目标设置以在没有主机应用程序的情况下运行。

Select your project --> then test target --> Set the host application to none.

选择您的项目 --> 然后测试目标 --> 将宿主应用程序设置为无。

enter image description here

在此处输入图片说明

2. Install xctool , if you don't have it.

2.安装 xctool ,如果你没有它。

brew install xctool

3. Run the tests using terminal with xctool.

3. 使用带有 xctool 的终端运行测试。

xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator

回答by Benjohn

Excellent answersabove that suggest dynamically changing the application delegate at run time.

上面的优秀答案建议在运行时动态更改应用程序委托。

The small modification I make is to detect a unit test runby 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.

我所做的小修改是通过查询来检测单元测试运行NSProcessInfo。优点是您不需要有一个可以检测到单元测试是否正在运行的类。

    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 environmentDictionaryis the path to your unit tests bundle and is set up by Xcode.

@"XCInjectBundleInto"物业environmentDictionary是通向你的单元测试包中,由Xcode的建立。

回答by sean woodward

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.

是的,您的测试目标将具有对应用程序目标的目标依赖性,因此当您按 Cmd+U 或 Cmd+Shift+U 时将构建应用程序目标。

回答by Francois Nadeau

You can do that by setting the Host Application to None in your Tests Target.

您可以通过在测试目标中将主机应用程序设置为无来实现。

enter image description here

在此处输入图片说明

回答by estemendoza

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

我使用 Tomasz Bak 的方法加上一些 dwb 答案的代码并得出以下结论:

- (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
    ....
}