news 2026/4/23 17:30:44

app稳定性测试-iOS篇

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
app稳定性测试-iOS篇

稳定性测试:测试应用程序在长时间运行过程中是否存在内存泄漏、崩溃等问题,以确保应用程序具有较高的稳定性和可靠性。

对于安卓端,官方提供了很好的稳定性测试工具:monkey。相比较而言,iOS则没有,而且当前网络上似乎也没有很好的第三方工具可以使用,因此只能自己写了。

我们要开发的iOS稳定性测试程序,应该至少包含以下内容:

  • 持续随机触发UI事件

  • 崩溃重启,测试不中断

  • 日志记录

首先我们确定以上设想的可行性,然后再制定实施方案。在iOS原生开发语言swift和object-C中提供了可进行单元测试和UI测试的XCTest框架,而同样可进行移动端UI测试的第三方框架还有Appium等,但相比较第三方的开源框架,原生的XCTest框架性能更好且更稳定,因此这里我们选择基于swift语言和XCTest框架来开发。

XCTest框架提供了非常全面的启动App和UI操作相关的API接口, 因此1、2两点完全可以实现,当然第三点的日志记录的实现也同样不会有什么问题。接下来就是具体实施了。

首先,我们创建一个用来执行测试的主类:StabilityTestRunner,然后再编写代码去实现以上三点。

持续随机触发UI事件

让我们拆分一下,随机触发UI事件,实际上包含两部分:随机UI元素和随机的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事件有一定的了解,具体使用可查看完整工程中的示例。

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 3:36:41

【必收藏】2026年AI大模型学习路线图与资源包,含300+面试题+1200+工具

本文提供2026年AI大模型全面学习资源包&#xff0c;包括系统学习路线图、GeekAGI知识库、1200AI工具与框架、主流应用教程、开源项目案例、300道大厂面试真题及行业研究报告。资源由资深AI专家整理&#xff0c;贴合2026年AI技术迭代趋势&#xff0c;适合初学者及进阶开发者&…

作者头像 李华
网站建设 2026/4/23 15:30:31

白蚁监测防治系统:可视化数据平台,蚁害动态一目了然

白蚁监测防治系统的可视化数据平台是一种集成物联网、大数据、GIS地图与智能分析技术的数字化管理工具&#xff0c;通过直观展示蚁害动态、实时预警风险、辅助科学决策&#xff0c;实现白蚁防治的精准化、智能化和可视化。 以下是其核心功能、技术特点、应用场景及优势的详细介…

作者头像 李华
网站建设 2026/4/23 12:56:27

救命神器10个AI论文写作软件,继续教育学生轻松搞定论文!

救命神器10个AI论文写作软件&#xff0c;继续教育学生轻松搞定论文&#xff01; 论文写作的“救星”正在悄然改变你的学习方式 在继续教育的学习过程中&#xff0c;论文写作往往成为许多学生最头疼的问题。无论是选题、大纲搭建&#xff0c;还是初稿撰写和降重处理&#xff0c;…

作者头像 李华
网站建设 2026/4/23 5:36:26

Node.js 编程实战:部署 Node.js 应用 —— Docker 容器化部署

随着 Node.js 应用从开发环境迁移到生产环境&#xff0c;传统的直接部署方式&#xff08;如裸机、PM2 守护进程&#xff09;虽然可行&#xff0c;但在环境一致性、快速部署、可扩展性上仍存在一定局限。 Docker 容器化部署提供了一种轻量、可移植、环境一致的解决方案&#xf…

作者头像 李华
网站建设 2026/4/22 23:46:51

WAF性能优化:如何平衡安全防护与网站访问速度?

优化规则集精简WAF规则库&#xff0c;仅保留与业务相关的防护规则。禁用不必要的规则或调整为仅记录模式&#xff0c;减少误报和性能开销。定期分析日志&#xff0c;移除长期未触发的冗余规则。启用缓存机制对静态内容和频繁访问的页面启用WAF缓存。配置合适的缓存时间&#xf…

作者头像 李华