到目前为止,我已经通过在模拟器中运行来手动测试我的 iOS/Swift 项目。现在,我已将单元测试目标添加到项目中,以包含自动化测试。
我惊讶地发现,在模拟器中运行默认目标和运行测试目标都会影响相同的数据/相同的应用程序实例。
例如,使用 SQLite 数据库和 UserDefaults 来保存一些数据和设置。当将数据写入数据库或更改测试内 UserDefaults 中的设置时,这些更改在模拟器中运行应用程序时也可见(反之亦然)。
我认为因为测试是在单独的目标中,所以将使用单独的数据。事实并非如此。
是否可以将测试目标设置为不干扰应用目标?
最佳答案
背景
这肯定是一个很棒的功能,但目前还不可能。这是因为单元测试并不完全是一个完整的 iOS 应用程序目标。相反,它只是将主应用程序目标托管为被测系统并测试其代码。
下面是单元测试目标的“常规”设置选项卡的屏幕截图。看到它实际上托管主应用程序目标,而不是主应用程序目标的克隆/变体。
您可以使用以下代码来解决此限制,该代码检查应用程序是否正在进行单元测试。
extension UIApplication{
var isTesting: Bool {
#if DEBUG
if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil {
return true
}
return false
#else
return false
#endif
}
}
请注意,我添加了“#if DEBUG
”条件编译标记,以防止在发布版本中评估进程信息。
下面我针对您的情况提出了两种解决方法。
SQLite 的解决方法
您可以使用此扩展来使用两个不同的数据库名称,具体取决于是否正在执行单元测试。
import SQLite
do {
let dbName = UIApplication.shared.isTesting ? "db_test" : "db"
let path = "path/to/\(dbName).sqlite"
let db = try Connection(path)
// ...
}
catch{
print(error)
}
用户默认值的解决方法
使用两组数据进行正常应用执行和测试并不那么简单。建议的解决方法引入了两个与 UserDefaults
中的标准 setValue
和 value
方法类似的新方法。这些特殊版本根据是否正在运行单元测试,在键后附加“_test”后缀。效果是正常设置不会被单元测试设置修改。
extension UserDefaults{
private var testKeySuffix: String{
return UIApplication.shared.isTesting ? "_test" : ""
}
func safeSetValue(_ value: Any?, forKey key: String){
UserDefaults.standard.setValue(value, forKey: "\(key)\(testKeySuffix)")
}
func safeValue(forKey key: String) -> Any?{
return UserDefaults.standard.value(forKey: "\(key)\(testKeySuffix)")
}
}
上述扩展可以按如下方式使用。
// UserDefaults.standard.setValue("123", forKey: "myKey")
UserDefaults.standard.safeSetValue("123", forKey: "myKey")
// let str = UserDefaults.standard.value(forKey: "myKey")
let str = UserDefaults.standard.safeValue(forKey: "myKey")
关于ios - 如何在与默认目标中的数据不同的数据/应用程序(SQLite db、UserDefaults)上运行 Xcode 测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69452935/