有没有办法在Xcode 7中的Swift XCTest UI中的testing之间重置应用程序?
在XCTest中是否有API调用,我可以放入setUP()或tearDown()来重置testing之间的应用程序? 我查看了XCUIApplication的点语法,我只看到了.launch()
OR有没有办法在Swift中调用shell脚本? 然后我可以调用xcrun中间的testing方法来重置模拟器。
您可以添加一个“运行脚本”阶段在testing目标中构build阶段,然后在运行unit testing之前卸载应用程序,但不幸的是 , 这不在testing用例之间 。
/usr/bin/xcrun simctl uninstall booted com.mycompany.bundleId
更新
在testing之间,你可以在tearDown阶段通过Springboard 删除应用程序 。 虽然,这确实需要使用XCTest的私有头文件。 ( 在这里可以从Facebook的WebDriverAgent获得标题转储。)
以下是Springboard类的一些示例代码,通过点击并按住从Springboard删除应用程序:
import XCTest class Springboard { static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard") /** Terminate and delete the app via springboard */ class func deleteMyApp() { XCUIApplication().terminate() // Resolve the query for the springboard rather than launching it springboard.resolve() // Force delete the app from the springboard let icon = springboard.icons["MyAppName"] if icon.exists { let iconFrame = icon.frame let springboardFrame = springboard.frame icon.pressForDuration(1.3) // Tap the little "X" button at approximately where it is. The X is not exposed directly springboard.coordinateWithNormalizedOffset(CGVectorMake((iconFrame.minX + 3) / springboardFrame.maxX, (iconFrame.minY + 3) / springboardFrame.maxY)).tap() springboard.alerts.buttons["Delete"].tap() } } }
接着:
override func tearDown() { Springboard.deleteMyApp() super.tearDown() }
私人头文件被导入Swift桥头。 您需要导入:
// Private headers from XCTest #import "XCUIApplication.h" #import "XCUIElement.h"
此时,Xcode 7&8和Simulator中的公共API没有任何方法可以从setUp()
和tearDown()
XCText
子类调用到模拟器的“Reset Contents and Settings”。
还有其他可能的方法使用公共API:
-
应用程序代码 添加一些
myResetApplication()
应用程序代码,使应用程序处于已知状态。 但是,设备(模拟器)状态控制受应用程序沙箱的限制,这在应用程序之外帮助不大。 这种方法对于清除应用程序可控持久性是可以的。 -
Shell脚本 。 从shell脚本运行testing。 使用
xcrun simctl erase all
或xcrun simctl uninstall <device> <app identifier>
或每次testing运行之间的类似操作来重置模拟器(或卸载应用程序) 。 请参阅StackOverflow:“如何从命令行重置iOS模拟器?”
macos> xcrun simctl --help # can uninstall a single application macos> xcrun simctl uninstall --help # Usage: simctl uninstall <device> <app identifier>
- Xcode架构操作 。 添加
xcrun simctl erase all
(或xcrun simctl erase <DEVICE_UUID>
)或类似的schemetesting部分。 select产品>计划>编辑计划…菜单。 展开计划testing部分。 在Test部分下selectPre-actions。 点击(+)添加“新build脚本操作”。 命令xcrun simctl erase all
可以直接input而不需要任何外部脚本。
调用选项1.应用程序代码重置应用程序:
A. 应用程序UI 。 [UItesting]提供重置button或其他UI操作,以重置应用程序。 UI元素可以通过XCUIApplication
例程setUp()
, tearDown()
或testSomething()
中的XCTest
来执行。
B. 启动参数 。 [UItesting]正如Victor Ronin指出的,一个参数可以从testingsetUp()
传递…
class AppResetUITests: XCTestCase { override func setUp() { // ... let app = XCUIApplication() app.launchArguments = ["MY_UI_TEST_MODE"] app.launch()
…被AppDelegate
收到…
class AppDelegate: UIResponder, UIApplicationDelegate { func application( …didFinishLaunchingWithOptions… ) -> Bool { // ... let args = NSProcessInfo.processInfo().arguments if args.contains("MY_UI_TEST_MODE") { myResetApplication() }
C. Xcodescheme参数 。 [用户界面testing,unit testing]select产品>scheme>编辑scheme…菜单。 展开Scheme Run部分。 (+)添加一些像MY_UI_TEST_MODE
参数。 该参数将在NSProcessInfo.processInfo()
可用。
// ... in application let args = NSProcessInfo.processInfo().arguments if args.contains("MY_UI_TEST_MODE") { myResetApplication() }
直接电话 。 [unit testing]unit testing包被注入正在运行的应用程序,并可以直接调用应用程序中的一些myResetApplication()
例程。 警告:默认unit testing在主屏幕加载后运行。 请参阅testing加载顺序但是,UItesting包作为被testing应用程序的外部进程运行。 所以,unit testing中的工作在UItesting中会出现链接错误。
class AppResetUnitTests: XCTestCase { override func setUp() { // ... Unit Test: runs. UI Test: link error. myResetApplication() // visible code implemented in application
更新为swift 3.1 / xcode 8.3
在testing目标中创build桥接头:
#import <XCTest/XCUIApplication.h> #import <XCTest/XCUIElement.h> @interface XCUIApplication (Private) - (id)initPrivateWithPath:(NSString *)path bundleID:(NSString *)bundleID; - (void)resolve; @end
更新了Springboard类
class Springboard { static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")! static let settings = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.Preferences")! /** Terminate and delete the app via springboard */ class func deleteMyApp() { XCUIApplication().terminate() // Resolve the query for the springboard rather than launching it springboard.resolve() // Force delete the app from the springboard let icon = springboard.icons["{MyAppName}"] /// change to correct app name if icon.exists { let iconFrame = icon.frame let springboardFrame = springboard.frame icon.press(forDuration: 1.3) // Tap the little "X" button at approximately where it is. The X is not exposed directly springboard.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX, dy: (iconFrame.minY + 3) / springboardFrame.maxY)).tap() springboard.alerts.buttons["Delete"].tap() // Press home once make the icons stop wiggling XCUIDevice.shared().press(.home) // Press home again to go to the first page of the springboard XCUIDevice.shared().press(.home) // Wait some time for the animation end Thread.sleep(forTimeInterval: 0.5) let settingsIcon = springboard.icons["Settings"] if settingsIcon.exists { settingsIcon.tap() settings.tables.staticTexts["General"].tap() settings.tables.staticTexts["Reset"].tap() settings.tables.staticTexts["Reset Location & Privacy"].tap() settings.buttons["Reset Warnings"].tap() settings.terminate() } } } }
你可以问你的应用程序来“清理”自己
- 您使用
XCUIApplication.launchArguments
来设置一些标志 -
在AppDelegate你检查
如果NSProcessInfo.processInfo()。arguments.contains(“YOUR_FLAG_NAME_HERE”){//做一个清理}
我使用了@Chase Holland 答案,并使用相同的方法更新了Springboard类,以使用“设置”应用重置内容和设置。 这在您需要重置权限对话框时非常有用。
导入XCTest class Springboard { static let springboard = XCUIApplication(privateWithPath:nil,bundleID:“com.apple.springboard”) static let settings = XCUIApplication(privateWithPath:nil,bundleID:“com.apple.Preferences”) / ** 通过跳板终止并删除应用程序 * / class func deleteMyApp(){ XCUIApplication()。结束() //parsing跳板的查询,而不是启动它 springboard.resolve() //从跳板强制删除应用程序 让图标= springboard.icons [“MyAppName”] 如果icon.exists { 让iconFrame = icon.frame 让springboardFrame = springboard.frame icon.pressForDuration(1.3) //点击大约在哪里的小“X”button。 X不直接暴露 ()(iconFrame.minX + 3)/ springboardFrame.maxX,(iconFrame.minY + 3)/ springboardFrame.maxY))。tap() springboard.alerts.buttons [ “删除”]。轻按() //按回家一次使图标停止摇摆 XCUIDevice.sharedDevice()。轻触式(。家里) //再按回家跳到跳板的第一页 XCUIDevice.sharedDevice()。轻触式(。家里) //等一段时间animation结束 NSThread.sleepForTimeInterval(0.5) 让settingsIcon = springboard.icons [“设置”] 如果settingsIcon.exists { settingsIcon.tap() settings.tables.staticTexts [ “常规”]。轻按() settings.tables.staticTexts [ “复位”]。轻按() settings.tables.staticTexts [“Reset Location&Privacy”]。tap() settings.buttons [“Reset Warnings”]。tap() settings.terminate() } } } }
我使用@ ODM 答案 ,但修改它为Swift 4工作。注意:一些S / O答案不区分Swift版本,有时有相当根本的区别。 我已经在iPhone 7模拟器和iPad Air模拟器上进行了纵向testing,并且适用于我的应用程序。
斯威夫特4
import XCTest import Foundation class Springboard { let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let settings = XCUIApplication(bundleIdentifier: "com.apple.Preferences") /** Terminate and delete the app via springboard */ func deleteMyApp() { XCUIApplication().terminate() // Resolve the query for the springboard rather than launching it springboard.activate() // Rotate back to Portrait, just to ensure repeatability here XCUIDevice.shared.orientation = UIDeviceOrientation.portrait // Sleep to let the device finish its rotation animation, if it needed rotating sleep(2) // Force delete the app from the springboard // Handle iOS 11 iPad 'duplication' of icons (one nested under "Home screen icons" and the other nested under "Multitasking Dock" let icon = springboard.otherElements["Home screen icons"].scrollViews.otherElements.icons["YourAppName"] if icon.exists { let iconFrame = icon.frame let springboardFrame = springboard.frame icon.press(forDuration: 2.5) // Tap the little "X" button at approximately where it is. The X is not exposed directly springboard.coordinate(withNormalizedOffset: CGVector(dx: ((iconFrame.minX + 3) / springboardFrame.maxX), dy:((iconFrame.minY + 3) / springboardFrame.maxY))).tap() // Wait some time for the animation end Thread.sleep(forTimeInterval: 0.5) //springboard.alerts.buttons["Delete"].firstMatch.tap() springboard.buttons["Delete"].firstMatch.tap() // Press home once make the icons stop wiggling XCUIDevice.shared.press(.home) // Press home again to go to the first page of the springboard XCUIDevice.shared.press(.home) // Wait some time for the animation end Thread.sleep(forTimeInterval: 0.5) // Handle iOS 11 iPad 'duplication' of icons (one nested under "Home screen icons" and the other nested under "Multitasking Dock" let settingsIcon = springboard.otherElements["Home screen icons"].scrollViews.otherElements.icons["Settings"] if settingsIcon.exists { settingsIcon.tap() settings.tables.staticTexts["General"].tap() settings.tables.staticTexts["Reset"].tap() settings.tables.staticTexts["Reset Location & Privacy"].tap() // Handle iOS 11 iPad difference in error button text if UIDevice.current.userInterfaceIdiom == .pad { settings.buttons["Reset"].tap() } else { settings.buttons["Reset Warnings"].tap() } settings.terminate() } } } }