Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/36.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/117.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Iphone 在Xcode中进行单元测试,它是否运行应用程序?_Iphone_Ios_Xcode_Unit Testing - Fatal编程技术网

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

Iphone 在Xcode中进行单元测试,它是否运行应用程序?,iphone,ios,xcode,unit-testing,Iphone,Ios,Xcode,Unit Testing,我遇到了一个以前从未遇到过的奇怪问题 当您使用cmd+U来运行单元测试(例如OCUnit)时,它是否真的调用main.m,重新启动appDelegate并运行应用程序,就好像您按下了cmd+R一样 我之所以问这个问题,是因为我在这个数据层后面使用CoreData。我在测试中成功地模拟了数据层,但一旦我实现了一个实际调用CoreData的getAll方法,app/xcode就会抛出一个关于托管对象模型不能为零的异常。我理解这一点,但我并不打算实际更新DataLayer类,我在mainviewcon

我遇到了一个以前从未遇到过的奇怪问题

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

我之所以问这个问题,是因为我在这个数据层后面使用CoreData。我在测试中成功地模拟了数据层,但一旦我实现了一个实际调用CoreData的getAll方法,app/xcode就会抛出一个关于托管对象模型不能为零的异常。我理解这一点,但我并不打算实际更新DataLayer类,我在mainviewcontroller loadView方法中设置了一个断点,它在这里调用DataLayer getAll方法。这与测试无关,因为这是一个模拟对象,但它显然在调用真实实例


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

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

应用程序实际上正在运行,但有一个技巧可以用来阻止它运行

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

这里是Sulthan答案的一个变体,它使用XTest,这是由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");
        }
    }
}
这将进入main.m,它应该位于标准项目布局中的支持文件下

然后在测试目录中添加:

TestAppDelegate.h


#import <Foundation/Foundation.h>

@interface TestAppDelegate : NSObject<UIApplicationDelegate>
@end
如果您正在使用Swift(您可能没有
main.c
),则必须执行以下步骤:

1:删除
AppDelegate.swift中的
@UIApplicationMain

import UIKit
class TestingAppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
}
2:创建一个空的
TestingAppDelegate.swift

import UIKit
class TestingAppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
}
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))
}

在Swift中,我更喜欢绕过
应用程序中的正常执行路径:didfishlaunchwithoptions

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
}

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

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

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

我找到了解决问题的另一种方法:

intmain(intargc,char*argv[])
{
@自动释放池{
返回UIApplicationMain(argc、argv、nil、({
![NSProcessInfo processInfo].environment[@“XCTestConfigurationFilePath”]?
@“AppDelegate”:
无
}));
}
}

从这里开始:

使用xCode 7和xCtool

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

为了让它工作

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

xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator
选择项目-->然后测试目标-->将主机应用程序设置为无

2。如果没有xctool,请安装它。

brew install xctool
3。使用终端和xctool运行测试。

xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator
上面的建议非常好,建议在运行时动态更改应用程序委托

我所做的小修改是通过查询
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);
        }
    }

环境字典
中的
@“XcinveBundleInto”
属性是单元测试捆绑包的路径,由Xcode设置。

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


非常好,我从来没有想过替换其他应用程序代理!我将把测试简化为
boolrunningtests=NSClassFromString(@“SenTestCase”)!=零@JonReid很好的补充。我从来没有想过要这样简化它!对于XCODE 5,BOOL inTests=(NSClassFromString(@“XTest”)!=nil;对我来说,
inTests
始终是
YES
即使我使用
cmd+r
cmd+u
@CarlJ运行应用程序,这可能是您的项目设置中的问题。请注意,OCTest/XCTest框架不应链接到主目标中。确保它只在test target.XCode 7(beta 5)+iOS9中-使用Process.argc,Process.gv而不是C_argc,C_argv使用这种方法,测试AppDelegate还会导致脚本加载吗?如果是这样,这可以避免吗?完全正常-是的,它仍然可以加载。这是有办法的,但我想找一个更好的办法。复制正在测试的目标,让我们称之为myApp和复制myApp copy。取消选中myApp复制目标的Main.storyboard的成员资格。添加名为Main.storyboard的新情节提要,并使其成为myApp copy目标的成员。更改myApp测试目标,使其测试myApp copy而不是myApp。现在将使用空的Main.storyboard,并且不会实例化任何视图控制器。不过,我想找到一种更简单的方法。不过,这些测试不会在iOS应用程序的上下文中运行,很多事情都不起作用。例如钥匙链和核心数据。如果你有UI测试,这根本不起作用。这对单元测试是有效的。这对我来说非常有效,而且似乎是最简单的解决方案!!非常感谢。