ios平台下自动化测试工具概览
表中没有列的内容是稳定性,实践中看来上述除UITesting之外的工具稳定性都相对一般,会出现自身框架导致的各种闪退,以及性能越来越差的问题。 因此iOS平台上除了Monkey测试采用了自动化方式,以及部分性能测试轻度使用了一些自动化工具,大部分功能测试还是依赖于人的操作。
UITestIng实例
import Foundation
import XCTest
class UITestDemo_UI_Tests: 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.
XCUIApplication().launch()
}
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.
testLoginView()
}
func testLoginView() {
let app = XCUIApplication()
// 由于UITextField的id有问题,所以只能通过label的方式遍历元素来读取
let nameField = app.textFields["name"]
if self.canOperateElement(nameField) {
nameField.tap()
nameField.typeText("xiaoming")
}
let psdField = app.secureTextFields["password"]
if self.canOperateElement(psdField) {
psdField.tap()
psdField.typeText("1234321")
}
// 通过UIButton的预设id来读取对应的按钮
let loginBtn = app.buttons["Login"]
if self.canOperateElement(loginBtn) {
loginBtn.tap()
}
// 开始一段延时,由于真实的登录是联网请求,所以不能直接获得结果,demo通过延时的方式来模拟联网请求
let window = app.windows.elementBoundByIndex(0)
if self.canOperateElement(window) {
// 延时3秒, 3秒后如果登录成功,则自动进入信息页面,如果登录失败,则弹出警告窗
window.pressForDuration(3)
}
// alert的id和labe都用不了,估计还是bug,所以只能通过数量判断
if app.alerts.count > 0 {
// 登录失败
app.alerts.collectionViews.buttons["确定"].tap()
let clear = app.buttons["Clear"]
if self.canOperateElement(clear) {
clear.tap()
if self.canOperateElement(nameField) {
nameField.tap()
nameField.typeText("sun")
}
if self.canOperateElement(psdField) {
psdField.tap()
psdField.typeText("111111")
}
if self.canOperateElement(loginBtn) {
loginBtn.tap()
}
if self.canOperateElement(window) {
// 延时3秒, 3秒后如果登录成功,则自动进入信息页面,如果登录失败,则弹出警告窗
window.pressForDuration(3)
}
self.loginSuccess()
}
} else {
// 登录成功
self.loginSuccess()
}
}
func loginSuccess() {
let app = XCUIApplication()
let window = app.windows.elementAtIndex(0)
if self.canOperateElement(window) {
// 延时1秒, push view需要时间
window.pressForDuration(1)
}
self.testInfo()
}
func testInfo() {
let app = XCUIApplication()
let window = app.windows.elementAtIndex(0)
if self.canOperateElement(window) {
// 延时2秒, 加载数据需要时间
window.pressForDuration(2)
}
let modifyBtn = app.buttons["modify"];
modifyBtn.tap()
let sexSwitch = app.switches["sex"]
sexSwitch.tap()
let incrementButton = app.buttons["Increment"]
incrementButton.tap()
incrementButton.tap()
incrementButton.tap()
app.buttons["Decrement"].tap()
let textView = app.textViews["feeling"]
textView.tap()
app.keys["Delete"].tap()
app.keys["Delete"].tap()
textView.typeText(" abc ")
// 点击空白区域
let clearBtn = app.buttons["clearBtn"]
clearBtn.tap()
// 保存数据
modifyBtn.tap()
window.pressForDuration(2)
let messageBtn = app.buttons["message"]
messageBtn.tap();
// 延时1秒, push view需要时间
window.pressForDuration(1)
self.testMessage()
}
func testMessage() {
let app = XCUIApplication()
let window = app.windows.elementAtIndex(0)
if self.canOperateElement(window) {
// 延时2秒, 加载数据需要时间
window.pressForDuration(2)
}
let table = app.tables
table.childrenMatchingType(.Cell).elementAtIndex(8).tap()
table.childrenMatchingType(.Cell).elementAtIndex(1).tap()
}
func getFieldWithLbl(label:String) -> XCUIElement? {
var _:XCUIElement? = nil
return self.getElementWithLbl(label, type: XCUIElementType.TextField)
}
func getElementWithLbl(label:String, type:XCUIElementType) -> XCUIElement? {
let app = XCUIApplication()
let query = app.descendantsMatchingType(type)
var result:XCUIElement? = nil
let num=query.count
print(num)
print("****************\n")
print(label)
print("***********************\n")
for i in 0..<num {
let element:XCUIElement = query.elementBoundByIndex(i)
let subLabel:String? = element.label;
if subLabel != nil {
if subLabel == label {
result = element
}
}
}
return result
}
func canOperateElement(element:XCUIElement?) -> Bool {
if element != nil {
if element!.exists {
return true
}
}
return false
}
}
参考
iOS 9 学习系列: UI Testing
Xcode7 UI自动化测试详解 带demo UITests
UITesting存在的问题:只适用于ios9.0以上的机器,以下的机器不能运行
同时存在不同的模拟器,比如iphon6,iphon6s等,有的可以正常运行,有的却不能等情况,不知道是模拟器的bug,还是程序的bug
ios UI Automation
UI Automation是黑盒测试,基于javascript语言。
实现要点
- 标签上要加上accessibilty为true
- 要选中user interaction enabled
- 可以通过setting-general-accessiibilty中的工具探测id
var target = UIATarget.localTarget();
var inputField = target.frontMostApp().mainWindow().textFields()["theinput"];
inputField.setValue("hifdssdf");
if (inputField.value() != "hi") UIALogger.logFail("The Input Field was NOT able to be set with the string!");
else UIALogger.logPass("The Input Field was able to be set with the string!");
target.frontMostApp().mainWindow().buttons()["Jumblify Button"].tap();
target.delay(2);
inputField.setValue("hi");
target.delay(10);
target.frontMostApp().mainWindow().buttons()["Jumblify Button"].tap();
var target = UIATarget.localTarget();
var inputField = target.frontMostApp().mainWindow().textFields()["theinput"];
inputField.setValue("hi");
if (inputField.value() != "hi") UIALogger.logFail("The Input Field was NOT able to be set with the string!");
else UIALogger.logPass("The Input Field was able to be set with the string!");
var button = target.frontMostApp().mainWindow().buttons()["Jumblify Button"];
button.tap();
target.frontMostApp().mainWindow().logElementTree();
var stringResult = target.frontMostApp().mainWindow().staticTexts()["ih"];
if (! stringResult.isValid()) UIALogger.logFail("The output text was NOT set with the correctly reversed string!");
else UIALogger.logPass("The output text was set with the correctly reversed string!");
使用命令行运行
如果你想让你的测试代码自动的运行起来,你还可以通过命令行来启动测试。其实,我比较推荐这种方式,而不是使用Instruments的图形界面程序。因为,Instruments的图形界面程序比较慢,而且即使你的测试代码跑完了它也还是会一直运行着。而通过命令行来启动和运行测试代码更快,它会在跑完测试后自动的停止。
为了可以在命令行终端运行你的脚本,你需要知道你设备的UDID和类型:
instruments -w your_ios_udid -t
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate
name_of_your_app -e UIASCRIPT absolute_path_to_the_test_file
例如,使用我自己的机子,就这么写的:
instruments -w a2de620d4fc33e91f1f2f8a8cb0841d2xxxxxxxx -t
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate
TestAutomation -e UIASCRIPT
/Users/jc/Documents/Dev/TestAutomation/TestAutomation/TestUI/Test-2.js
一个小提示,不要忘了关闭你设备的密码验证,否则你会看到这样的日志信息的:remote exception encountered : ’device locked : Failed to launch process with bundle identifier ’com.manbolo.testautomation’. 的确,因为UIAutomation根本不知道你的密码啊。
命令行终端同样可以在模拟器上使用,但你需要知道待测应用程序在文件系统中的绝对路径。模拟器将目录~/Library/Application Support/iPhone Simulator/5.1/ “模拟”成了设备的文件系统。在这个目录下,你可以找到一个包含装在模拟器上的所有应用程序的沙盒的Applications文件夹。定位到TestAutomation程序的目录,然后:
instruments -t /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate "/Users/jc/Library/Application Support/iPhone Simulator/5.1/Applications/C28DDC1B-810E-43BD-A0E7-C16A680D8E15/TestAutomation.app" -e UIASCRIPT /Users/jc/Documents/Dev/TestAutomation/TestAutomation/TestUI/Test-2.js
最后,如果你没有指定日志输入到哪里的话,你的测试结果会被放到你命令行当前指定(工作)的目录下。你可以通过加入 -e UIARESULTSPATH results_path 参数来指定日志输入目录。
我没有成功的将多个测试脚本并行着在命令行中运行起来。但是你可以将你的测试脚本串连进来,有一整晚去跑它,这样就真正的实现了“在你睡着的时候”,就完成了对应用程序的测试。
XCODE7模拟器与模拟器中app的路径
在Xcode 7中, 模拟器的位置改变为:
/Users/username/Library/Developer/CoreSimulator/Devices
在此目录下,有许多文件夹这就是模拟器啦:
- 在Terminal中使用如下命令:
xcrun simctl list
可以知道uuid与版本的对应关系
比如,iOS 9.3下,iPhone 6s:
iPhone 6s (60B8F826-8241-498A-A180-35C3F4F59562) (Booted)
因此,Application目录在:
/Users/username/Library/Developer/CoreSimulator/Devices/D2A94C2D-3216-4737-A502-5B64B38F6124/data/Containers/Data/Application/
参见: Xcode 7中模拟器的位置
参考链接
如何使用UIAutomation进行iOS 自动化测试(Part II)
Introduction to iOS Testing With UI Automation
如何使用UIAutomation进行iOS 自动化测试