根据 Xcode UI 测试是否 运行 定义全局变量

Defining global variables according to whether or not Xcode UI tests are running

如何根据 Xcode UI 测试是否 运行 来定义 global 变量?我正在尝试这样做:

#if UITESTS
    let api = StubbedAPI()
#else 
    let api = RealAPI()
#endif

这些是全局变量,所以我不能在文件范围内调用 NSProcessInfo.processInfo().environmentNSProcessInfo.processInfo().arguments

UI 测试目标 运行 作为独立于您的应用程序的进程。这意味着您不能在测试目标中设置预处理器宏并期望应用知道它们。测试与应用程序通信的唯一方法是通过您提到的两个 processInfo 设置。

使用这些是动态的,而您提出的解决方案是静态的。但是,您仍然可以使用 Apple 提供的工具来完成您想要做的事情。

首先,创建 StubbedAPI 和 RealAPI 都遵守的协议。

protocol API {
    // ... //
}

class RealAPI: API {
    // ... //
}

class StubbedAPI: API {
    // ... //
}

接下来,创建配置 class。这将用于告诉您的代码在 运行 时使用哪个 API。

struct Config {
    var api: APIProtocol { 
        get { 
            return UITesting() ? RealAPI() : StubbedAPI()
        }
    }
}

private func UITesting() -> Bool {
    return NSProcessInfo.processInfo().arguments.contains("UI-TESTING")
}

然后,通过配置检索对 API 实现的引用。

class FooService {
    private let api = Config().api;
}

最后,在 UI 测试下启动应用程序之前设置 processInfo 参数。

class UITests: TestCase {
    let app = XCUIApplication()

    override func setUp() {
        super.setUp()
        app.launchArguments = ["UI-TESTING"]
        app.launch()
    }
}

api 属性 将在 运行 生产代码和 UI 测试下的存根代码时设置为真正的 API。

这种方法有一些缺点。首先,您将实际的 "stubbed" API 引入到您的生产代码中。这对实际在生产中使用它的开发人员有潜在的不利影响。其次,您需要创建 API 协议以仅让一个 "real" 对象实现它。不幸的是,鉴于 UI 测试和 Swift.

的当前状态,这是我想出的最佳解决方案