如何检测iOS应用是否在UI测试模式下运行

56
我希望我的应用程序在运行UI测试时能够运行特殊代码(例如重置其状态)。我查看了在从UI Testing运行应用程序时设置的环境变量,但没有明显的参数可以区分应用程序正常运行和在UI Testing中运行。有没有办法找出呢?
两个解决方法我不太满意:
1. 设置XCUIApplication.launchEnvironment与某些变量,然后我稍后在应用程序中检查它。这不好,因为您必须在每个测试文件的setUp方法中设置它。我尝试从方案设置中设置环境变量,但在运行UI测试测试时,该设置不会传播到应用程序本身。 2. 检查环境变量__XPC_DYLD_LIBRARY_PATH不存在。这似乎很巧妙,可能只是因为我们的目标构建设置的巧合而起作用。
8个回答

60

我自己一直在做这方面的研究,也遇到过这个问题。最终我采用了 @LironYahdav 的第一个解决方法:

在你的UI测试中:

- (void)setUp
{
    [super setUp];

    XCUIApplication *app = [[XCUIApplication alloc] init];
    app.launchEnvironment = @{@"isUITest": @YES};
    [app launch];
}

在您的应用程序中:

NSDictionary *environment = [[NSProcessInfo processInfo] environment];
if (environment[@"isUITest"]) {
    // Running in a UI test
}

@JoeMasilotti的解决方案对于单元测试非常有用,因为它们与被测试的应用程序共享相同的运行时,但对于UI测试没有意义。


在Xcode 7中,这似乎对我不起作用。有其他的解决方法吗? - nemesis
4
发布环境现在是NSDictionary<String, String> Nemesis。你现在不能使用@YES,而必须使用字符串。 - Joey Clover
1
Swift 版本: let isUITest = NSProcessInfo.processInfo().environment["isUITest"] != nil - bbjay
我的AppDelegate是用Objective C编写的,我的测试是用Swift编写的。 这是我唯一有效的解决方案。 - RawKnee

28

我没有成功设置启动环境,但是通过设置启动参数让它工作了。

在你的测试中,在setUp()函数中添加:

let app = XCUIApplication()
app.launchArguments = ["testMode"]
app.launch()

在您的生产代码中添加如下检查:

let testMode =  NSProcessInfo.processInfo().arguments.contains("testMode")
if testMode {
  // Do stuff
}

已通过 Xcode 7.1.1 进行验证。


4
这对我似乎没有用。在 UI 测试中启动应用程序后,NSProcessInfo 参数中不包含测试字符串。 Xcode 7.3b3。 - Zev Eisenberg
在7.3中出现了相同的问题。我在设置中设置了launchArguments,但是如果我在下一行上断点,launchArguments为空。就好像它们没有被存储一样。 - drekka
启动参数与环境是不同的,我认为。按照苹果文档...NSProcessInfo有两个我们想要检查的进程信息属性。第一个是“arguments”:该进程的命令行参数字符串数组第二个是“environment”:该进程从中启动的环境中的变量名(键)及其值。您应注意到它们之间的主要区别,即“从中启动进程的环境”与“命令行参数”。 - TimD
因此,如果从命令行启动,则放置launchArguments是有效的,但您还必须在代码中检查这些launchArgs。 您不能检查processInfo()。环境字典并期望其中包含“arguments”。 这将在NSProcessInfo上的“arguments”属性中。 再次查看我在Apple Docs中的评论。 - TimD
1
较新版本的Xcode/Swift使用import FoundationProcessInfo.processInfo.arguments.contains("testMode")来检查应用程序中的标志。这个方法对我很有效。 - alexbhandari
显示剩余2条评论

7
您可以使用预处理宏来实现此功能。我发现您有几个选择:

新目标

复制应用程序的目标,并将其用作测试目标。此目标副本中的任何预处理宏在代码中都可访问。

缺点是您还需要向副本目标添加新类/资源,有时很容易忘记。

新构建配置

复制调试构建配置,将任何预处理宏设置为该配置,并将其用于您的测试(请参见下面的屏幕截图)。

一个小问题:每当您想要记录UI测试会话时,您需要更改运行以使用新的测试配置。

添加重复配置:

Add a duplicate conf

将其用于您的测试

Use it for your *Test*


6

根据之前的回答,Swift 3是基于it技术的。

class YourApplicationUITests: XCTestCase {

    override func setUp() {
        super.setUp()

        // Put setup code here. This method is called before the invocation of each test method in the class.

        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
        let app = XCUIApplication()
        app.launchArguments = ["testMode"]
        app.launch()

        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    func testExample() {
        // Use recording to get started writing UI tests.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
    }

}


extension UIApplication {
    public static var isRunningTest: Bool {
        return ProcessInfo().arguments.contains("testMode")
    }
}

那么只需要在您的代码中调用UIApplication.isRunningTest函数即可。

3
我刚刚添加了这个扩展。
 @available(iOS 9, *)
 extension XCUIApplication {

 func test(){
   launchEnvironment = ["TEST":"true"]
   launch()
  }
 }

所以我可以使用test()而不是launch()


3
在Swift 3中,您可以检查XCInjectBundleInto键,或以XC开头的内容。
let isInTestMode = ProcessInfo.processInfo.environment["XCInjectBundleInto"] != nil

这也适用于OS X操作系统。

@Phillip Otto - Oleander没有提到的一件事是在你的类中导入Foundation。 - Michael Eakins
1
检查 XCTestConfigurationFilePath 似乎有效。 - John Bushnell

1

我的解决方案与上面的Ciryon几乎相同,只是对于我的macOS基于文档的应用程序,我必须在参数名称前加连字符

let app = XCUIApplication()
app.launchArguments.append("-Testing")
app.launch() 

否则,“Testing”会被解释为启动应用程序时要打开的文档名称,因此我收到了错误警报:

enter image description here


1

不仅限于 UI 测试,而是针对测试一般情况下,为 ProcessInfo 创建一个扩展。

public extension ProcessInfo {
    static var isTesting: Bool {
        processInfo.environment["XCTestConfigurationFilePath"] != nil
    }
}

使用方法:

if ProcessInfo.isTesting {}
guard !ProcessInfo.isTesting else { return }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接