XCTest中是否有API調用,我可以放入setUP()或tearDown()來重置測試之間的應用程序?我查看了XCUIApplication的點語法,我只看到了.launch()有沒有辦法在Xcode 7中的Swift XCTest UI中的測試之間重置應用程序?
或者有沒有辦法在Swift中調用shell腳本?然後我可以調用xcrun中間的測試方法來重置模擬器。
XCTest中是否有API調用,我可以放入setUP()或tearDown()來重置測試之間的應用程序?我查看了XCUIApplication的點語法,我只看到了.launch()有沒有辦法在Xcode 7中的Swift XCTest UI中的測試之間重置應用程序?
或者有沒有辦法在Swift中調用shell腳本?然後我可以調用xcrun中間的測試方法來重置模擬器。
此時,在Xcode 7 & 8的公共API和模擬器不出現具有從setUp()
和tearDown()
XCText
可調用子類的任何方法,以「復位內容和設置」模擬器。
存在使用公共API其他可能的方法:
應用代碼。添加一些myResetApplication()
應用程序代碼以使應用程序處於已知狀態。但是,設備(模擬器)狀態控制受應用程序沙箱的限制......在應用程序之外沒有太多幫助。這種方法對於清除應用程序可控持久性是可以的。
Shell腳本。從shell腳本運行測試。在每次測試運行之間使用xcrun simctl erase all
或xcrun simctl uninstall <device> <app identifier>
或類似軟件重置模擬器(或卸載應用程序)。 see StackOverflow: "How can I reset the iOS Simulator from the command line?"
macos> xcrun simctl --help
# can uninstall a single application
macos> xcrun simctl uninstall --help
# Usage: simctl uninstall <device> <app identifier>
xcrun simctl erase all
(或xcrun simctl erase <DEVICE_UUID>
)或類似的計劃測試部分。選擇產品>計劃>編輯計劃...菜單。展開Scheme測試部分。選擇測試部分下的預先操作。點擊(+)添加「新建腳本動作」。命令xcrun simctl erase all
可以直接輸入,而不需要任何外部腳本。用於調用1.應用程序代碼重置應用選項:
A. 應用程序UI。 [用戶界面測試]提供重置按鈕或其他UI操作來重置應用程序。 UI元素可以通過XCTest
例程setUp()
,tearDown()
或testSomething()
中的XCUIApplication
來行使。
B. 啓動參數。 [UI試驗]如由Victor羅寧所指出的,一個參數可以從測試setUp()
傳遞...
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. Xcode方案參數。[UI測試,單元測試]選擇產品>方案>編輯方案...菜單。展開Scheme Run部分。 (+)添加一些參數,如MY_UI_TEST_MODE
。該參數將在NSProcessInfo.processInfo()
中提供。
// ... in application
let args = NSProcessInfo.processInfo().arguments
if args.contains("MY_UI_TEST_MODE") {
myResetApplication()
}
Z. 直接呼叫。 [單元測試]單元測試包被注入到正在運行的應用程序中,並可以直接在應用程序中調用一些myResetApplication()
例程。警告:默認單元測試在主屏幕加載後運行。 see Test Load Sequence但是,UI測試包作爲被測試應用程序的外部進程運行。所以,單元測試中的工作會在UI測試中出現鏈接錯誤。
class AppResetUnitTests: XCTestCase {
override func setUp() {
// ... Unit Test: runs. UI Test: link error.
myResetApplication() // visible code implemented in application
你可以問問你的應用程序,以 「清理」 本身
XCUIApplication.launchArguments
設置一些標誌在AppDelegate中您檢查
如果NSProcessInfo.processInfo()。 arguments.contains(「YOUR_FLAG_NAME_HERE」){ //在這裏做一個清理 }
這是向我瞭解launchArgruments方法邁出的巨大一步。謝謝你的洞察力。它引導我去http://nshipster.com/launch-arguments-and-environment-variables/ 請原諒我在這裏的noobness。如果我編輯該方案並創建啓動參數,以及如何設置新創建的參數的具體內容?我看到如何將它作爲標記傳遞給測試,但就像我的情況一樣,我想運行一個腳本來重置模擬器的狀態。你能否更詳細地解釋一下實際論證的創造? – JJacquet
@jermobileqa首先,不需要道歉。我在某種程度上與你相似。我今天從字面上開始使用新的UI測試。我正在研究如何解決這個問題。我目前在我的測試的setUp方法中設置了XCUIApplication.launchArguments,並在func應用程序的AppDelegate中檢查它。我沒有修改架構。因此,我只需使用Command + U從XCode運行測試,它將使用此參數,我的應用程序將清除它所保留的所有內容。 –
不幸的是,這不在測試用例之間,雖然,但在運行單元測試之前卸載應用程序,您可以添加「運行腳本」階段以構建測試目標階段。
/usr/bin/xcrun simctl uninstall booted com.mycompany.bundleId
更新
測試之間,你可以通過在拆卸階段的跳板刪除的應用。雖然,這確實需要使用來自XCTest的私有頭文件。 (報頭轉儲可從Facebook's WebDriverAgent here。)
下面是從一個跳板類一些示例代碼經由抽頭從跳板刪除應用和保持:
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"
這工作得很好。您只需將WebDriveAgent中的PrivateHeaders文件夾添加到您的項目中(無需添加全部內容),然後製作一個橋接標頭,以#import提供所需的標頭。 –
這應該是公認的答案。 –
很好的回答!有沒有更聰明的方法來獲取「MyAppName」?我試過使用'NSBundle-bundleWithIdentifier/Path',但測試應用程序沒有對應用程序包的引用。我的項目有很多目標,每個都有不同的名稱,我希望能夠跨所有目標使用Springboard類。 – Avi
我用@Chase荷蘭answer和更新的跳板類遵循相同的方法來重新使用設置應用程序的內容和設置。這在您需要重置權限對話框時非常有用。
import XCTest 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"] 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() // Press home once make the icons stop wiggling XCUIDevice.sharedDevice().pressButton(.Home) // Press home again to go to the first page of the springboard XCUIDevice.sharedDevice().pressButton(.Home) // Wait some time for the animation end NSThread.sleepForTimeInterval(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(privateWithPath:...)'在Swift 3中沒有公開,它看起來像? – buildsucceeded
@buildsucceeded你需要創建一個橋接頭並導入私有頭文件。檢查我的答案是否正確實施。 – JustinM
更新爲swift 3.1/xcode 8。3
在測試目標創建橋接報頭:
#import <XCTest/XCUIApplication.h>
#import <XCTest/XCUIElement.h>
@interface XCUIApplication (Private)
- (id)initPrivateWithPath:(NSString *)path bundleID:(NSString *)bundleID;
- (void)resolve;
@end
更新跳板類
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()
}
}
}
}
我用@ODManswer,但修改它用於夫特4. NB工作:一些S/O答案沒有區分Swift版本,這些版本有時具有相當根本性的差異。我已經在iPhone 7模擬器和iPad Air模擬器上進行了縱向測試,並且適用於我的應用。
斯威夫特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()
}
}
}
}
對於iOS模擬遊戲11的時候,我做了曾經如此輕微的修改,以挖掘「X」圖標,在這裏我們每修復@code猴建議挖掘。 Fix在10.3和11.2手機模擬器上運行良好。爲了記錄,我使用了swift 3.以爲我會通過一些代碼複製和粘貼以更輕鬆地找到修復。 :)
import XCTest
class Springboard {
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.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["My Test App"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard!.frame
icon.press(forDuration: 1.3)
springboard!.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3 * UIScreen.main.scale)/springboardFrame.maxX, dy: (iconFrame.minY + 3 * UIScreen.main.scale)/springboardFrame.maxY)).tap()
springboard!.alerts.buttons["Delete"].tap()
}
}
}
的可能的複製[我如何在Xcode 7 UI測試每次測試後重置應用程序數據?](http://stackoverflow.com/questions/32351149/how-do-i-reset-每個測試後的應用程序數據 - 用-xcode-7-ui-testing) –
有趣的我在找到這個問題時找不到這個問題。我責怪糟糕的查詢結果。任何人,隨時刪除這個「笨蛋」,我前一段時間解決了這個問題,使用快速通道/'gitlab-ci.yml'文件的優雅解決方案。 – JJacquet
聽起來很酷@JJacquet會檢查出來 –