稳定性测试:测试应用程序在长时间运行过程中是否存在内存泄漏、崩溃等问题,以确保应用程序具有较高的稳定性和可靠性。
我们要开发的iOS稳定性测试程序,应该至少包含以下内容:
- 持续随机触发UI事件
- 崩溃重启,测试不中断
- 日志记录
首先,我们创建一个用来执行测试的主类:StabilityTestRunner,然后再编写代码去实现以上三点。
一、持续随机触发UI事件
随机生成UI元素:
func randomElement(of types: [ElementType] -> XCUIElement? {
var allElement:[XCUIElement] = []
for type in types {
if !self.exists{
break
}
var elements: [XCUIElement]
if self.alerts.count > 0 {
elements = self.alerts.descendants(matching: type.allElementsBoundByIndex
}else {
elements = self.descendants(matching: type.allElementsBoundByIndex
}
let filteredElements = elements.filter { element in
if !element.exists {
return false
}
if !element.isHittable || !element.isEnabled {
return false // Filter out non clickable and blocked elements.
}
return true
}
allElement.append(contentsOf: filteredElements
}
return allElement.randomElement(
}
随机生成UI操作:
/**
Random execution of the given UI operation.
- parameter element: Page Elements.
- parameter actions: Dictionary objects containing different UI operations.
*/
private func performRandomAction(on element: XCUIElement, actions: [String: (XCUIElement -> (] {
let keys = Array(actions.keys
let randomIndex = Int.random(in: 0..<keys.count
let randomKey = keys[randomIndex]
let action = actions[randomKey]
if action == nil {
return
}
if !element.exists {
return
}
if !element.isHittable {
return
}
Utils.log("step\(currentStep: \(randomKey \(element.description"
action!(element
}
二、持续测试和崩溃重启
while !isTestingComplete{
// Randomly select page elements.
let element = app.randomElement(of: elementType
if element != nil {
currentStep += 1
takeScreenshot(element: element!
performRandomAction(on: element!, actions: actions // Perform random UI operations.
XCTWaiter(.wait(for: [XCTNSPredicateExpectation(predicate: NSPredicate(format: "self == %d", XCUIApplication.State.runningForeground.rawValue, object: app], timeout: stepInterval
if app.state != .runningForeground {
if app.state == .notRunning || app.state == .unknown {
Utils.saveImagesToFiles(images: screenshotData
Utils.saveImagesToFiles(images: screenshotOfElementData, name: "screenshot_element"
Utils.log("The app crashed. The screenshot before the crash has been saved in the screenshot folder."
}
app.activate(
}
}
}
三、日志记录
记录截图并标记UI元素:
private func takeScreenshot(element: XCUIElement {
let screenshot = app.windows.firstMatch.screenshot(.image
if screenshotData.count == 3 {
let minKey = screenshotData.keys.sorted(.first!
screenshotData.removeValue(forKey: minKey
}
let screenshotWithRect = Utils.drawRectOnImage(image: screenshot, rect: element.frame
screenshotData[currentStep] = screenshotWithRect.pngData(
let screenshotOfElement = element.screenshot(.pngRepresentation
if screenshotOfElementData.count == 3 {
let minKey = screenshotOfElementData.keys.sorted(.first!
screenshotOfElementData.removeValue(forKey: minKey
}
screenshotOfElementData[currentStep] = screenshotOfElement
}
通过文本日志记录测试执行过程:
static func log(_ message: String {
print(message
let dateFormatter = DateFormatter(
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateString = dateFormatter.string(from: Date(
let fileManager = FileManager.default
do {
try fileManager.createDirectory(atPath: logSavingPath, withIntermediateDirectories: true, attributes: nil
} catch {
print("Error creating images directory: \(error"
}
var fileURL: URL
if #available(iOS 16.0, * {
fileURL = URL.init(filePath: logSavingPath.appendingPathComponent("log.txt"
} else {
fileURL = URL.init(fileURLWithPath: logSavingPath.appendingPathComponent("log.txt"
}
do {
try "\(dateString \(message".appendLineToURL(fileURL: fileURL
} catch {
print("Error writing to log file: \(error"
}
日志导出:
// To add the log files to the test results file, you can view it on your Mac. The test results file path: /User/Library/Developer/Xcode/DerivedData/AppStability-*/Logs.
let zipFile = "\(NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true[0]/Logs.zip"
let attachment = XCTAttachment(contentsOfFile: URL(fileURLWithPath: zipFile
attachment.name = "Logs"
attachment.lifetime = .keepAlways
// Add the "Logs.zip" file to the end of test result file.
add(attachment
Utils.log("The logs for test steps has been added to the end of test result file at /User/Library/Developer/Xcode/DerivedData/AppStability-*/Logs"
注:以上代码只是主体实现,了解相关细节可通过GitHub或Gitee查阅完整代码。
总结
总的来说实现起来并不是很困难,当然从程序使用角度而言,用户可自定义随机UI事件的UI元素范围和UI操作的范围以及测试执行的时长和时间间隔,因此需要对ios应用程序和Xcode的使用以及iOS UI事件有一定的了解,具体使用可查看完整工程中的示例。