MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C中的UIAutomation与自动化测试

2023-04-212.4k 阅读

1. 理解 UIAutomation

UIAutomation 是苹果提供的一套用于 iOS 应用自动化测试的框架。它允许开发者编写脚本,模拟用户在设备或模拟器上与应用进行交互,从而实现自动化测试流程。通过 UIAutomation,我们可以执行诸如点击按钮、输入文本、滑动屏幕等操作,验证应用的功能是否正常。

在 Objective-C 开发的 iOS 应用中,使用 UIAutomation 能够大幅提高测试效率,特别是在重复执行相同测试场景时,手动测试不仅耗时耗力,还容易出错。自动化测试可以确保每次执行的一致性,快速发现应用中的潜在问题。

2. 环境搭建

2.1 开发工具要求

要使用 UIAutomation 进行自动化测试,首先需要确保开发环境满足一定要求。我们需要安装最新版本的 Xcode,因为它包含了 UIAutomation 相关的工具和库。Xcode 为我们提供了创建、编辑和运行自动化测试脚本的集成环境。

2.2 创建测试项目

在 Xcode 中,创建一个新的 iOS 项目。选择“Single - View App”模板,填写项目名称和其他相关信息后点击“Create”。这个项目将作为我们自动化测试的目标应用。

2.3 配置测试目标

打开项目的“Build Phases”选项卡,在“Target Dependencies”中添加“UI Automation Instrument”。这一步确保了自动化测试工具能够正确地与我们的应用集成。

3. 认识 UIAutomation 脚本结构

3.1 基本结构

UIAutomation 脚本基于 JavaScript 编写,虽然我们是在 Objective - C 项目中使用它,但脚本语言是 JavaScript。一个基本的 UIAutomation 脚本结构如下:

function run() {
    // 测试代码逻辑放在这里
}

run 函数是脚本的入口点,所有的测试操作都在这个函数内部进行。

3.2 获取应用对象

在脚本中,我们首先需要获取要测试的应用对象。可以使用 UIATarget 类来实现:

function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
}

这里,UIATarget.localTarget() 获取本地设备或模拟器的目标对象,target.frontMostApp() 获取当前前台运行的应用对象。

4. 元素定位与操作

4.1 元素定位方法

在自动化测试中,准确地定位应用中的元素是关键。UIAutomation 提供了多种元素定位方法。

  • 通过标识符定位:如果在开发应用时为视图元素设置了 accessibilityIdentifier,可以通过它来定位元素。例如,假设有一个按钮的 accessibilityIdentifier 为“loginButton”,定位代码如下:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var loginButton = app.navigationBar().buttons()["loginButton"];
}
  • 通过类型和标签定位:可以根据元素的类型(如按钮、文本框等)和标签(accessibilityLabel)来定位。例如,定位一个标签为“用户名”的文本框:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var usernameTextField = app.textFields()["用户名"];
}

4.2 常见元素操作

定位到元素后,就可以对其进行各种操作。

  • 点击操作:对于按钮等可点击元素,可以使用 tap() 方法。例如:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var loginButton = app.navigationBar().buttons()["loginButton"];
    loginButton.tap();
}
  • 输入文本操作:对于文本框元素,使用 setValue() 方法输入文本。例如:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var usernameTextField = app.textFields()["用户名"];
    usernameTextField.setValue("testUser");
}

5. 屏幕导航与手势操作

5.1 屏幕导航

在应用中,常常需要进行页面之间的导航。例如,从一个视图控制器跳转到另一个视图控制器。可以通过点击导航栏上的按钮或其他触发导航的元素来实现。

假设应用中有一个导航栏,点击“详情”按钮跳转到详情页面:

function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var detailButton = app.navigationBar().buttons()["详情"];
    detailButton.tap();
}

5.2 手势操作

UIAutomation 支持多种手势操作,如滑动、缩放等。

  • 滑动操作:使用 scroll() 方法可以实现滑动屏幕。例如,向下滑动屏幕:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var scrollView = app.scrollViews()[0];
    scrollView.scroll({x: 0, y: 100}, {x: 0, y: 200});
}

这里,scroll() 方法的第一个参数是起始点坐标,第二个参数是结束点坐标。

  • 缩放操作:使用 pinch() 方法实现缩放。例如,放大操作:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var imageView = app.imageViews()[0];
    imageView.pinch({x: 100, y: 100}, {x: 120, y: 120}, 2);
}

pinch() 方法的前两个参数是两个手指的起始点坐标,第三个参数是缩放因子。

6. 断言与验证

6.1 断言的重要性

在自动化测试中,断言是验证应用功能是否正确的关键步骤。通过断言,我们可以判断某个操作是否达到预期结果,例如页面是否正确跳转、文本是否显示正确等。

6.2 常见断言类型

  • 验证元素是否存在:使用 UIATargetelementByType() 方法结合 exists() 方法来验证元素是否存在。例如,验证一个名为“登录成功”的标签是否存在:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var successLabel = app.staticTexts()["登录成功"];
    if (successLabel.exists()) {
        UIALogger.logPass("登录成功标签存在");
    } else {
        UIALogger.logFail("登录成功标签不存在");
    }
}
  • 验证文本内容:对于文本框、标签等元素,可以验证其显示的文本内容。例如,验证用户名输入框中的文本是否为“testUser”:
function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var usernameTextField = app.textFields()["用户名"];
    var text = usernameTextField.value();
    if (text === "testUser") {
        UIALogger.logPass("用户名文本正确");
    } else {
        UIALogger.logFail("用户名文本错误,实际为:" + text);
    }
}

7. 高级自动化测试技巧

7.1 处理弹窗

在应用中,常常会出现弹窗。处理弹窗需要先定位弹窗元素,然后进行相应操作。例如,处理一个提示弹窗:

function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var alert = app.alert();
    if (alert.exists()) {
        var okButton = alert.buttons()["确定"];
        okButton.tap();
    }
}

7.2 多场景测试

在实际应用中,可能有多种不同的测试场景。可以通过编写不同的测试函数来实现多场景测试,并在主 run 函数中依次调用这些函数。

function testLogin() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var usernameTextField = app.textFields()["用户名"];
    var passwordTextField = app.textFields()["密码"];
    var loginButton = app.navigationBar().buttons()["登录"];

    usernameTextField.setValue("testUser");
    passwordTextField.setValue("testPassword");
    loginButton.tap();

    var successLabel = app.staticTexts()["登录成功"];
    if (successLabel.exists()) {
        UIALogger.logPass("登录测试通过");
    } else {
        UIALogger.logFail("登录测试失败");
    }
}

function testLogout() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var logoutButton = app.navigationBar().buttons()["注销"];
    logoutButton.tap();

    var loginButton = app.navigationBar().buttons()["登录"];
    if (loginButton.exists()) {
        UIALogger.logPass("注销测试通过");
    } else {
        UIALogger.logFail("注销测试失败");
    }
}

function run() {
    testLogin();
    testLogout();
}

7.3 数据驱动测试

数据驱动测试允许我们使用不同的数据集来运行相同的测试场景。可以通过将测试数据存储在数组或 JSON 文件中来实现。

例如,使用数组存储不同的用户名和密码组合进行登录测试:

function run() {
    var target = UIATarget.localTarget();
    var app = target.frontMostApp();

    var userData = [
        {username: "user1", password: "pass1"},
        {username: "user2", password: "pass2"}
    ];

    for (var i = 0; i < userData.length; i++) {
        var usernameTextField = app.textFields()["用户名"];
        var passwordTextField = app.textFields()["密码"];
        var loginButton = app.navigationBar().buttons()["登录"];

        usernameTextField.setValue(userData[i].username);
        passwordTextField.setValue(userData[i].password);
        loginButton.tap();

        var successLabel = app.staticTexts()["登录成功"];
        if (successLabel.exists()) {
            UIALogger.logPass("使用 " + userData[i].username + " 登录测试通过");
        } else {
            UIALogger.logFail("使用 " + userData[i].username + " 登录测试失败");
        }
    }
}

8. 运行自动化测试

8.1 在 Xcode 中运行

在 Xcode 中,打开“Product”菜单,选择“Profile”。在弹出的“Choose a Profile Template”对话框中,选择“Automation”,然后点击“Choose”。

Xcode 会启动 Instruments 工具,并加载我们的应用和自动化测试脚本。在 Instruments 中,点击“Record”按钮开始运行测试脚本,测试过程中可以看到应用的操作步骤和断言结果。

8.2 命令行运行

除了在 Xcode 中运行,还可以通过命令行使用 instruments 工具运行自动化测试脚本。首先,确保 Xcode 命令行工具已经安装并配置好路径。

假设我们的自动化测试脚本文件名为 test.js,应用的 app 文件路径为 /path/to/MyApp.app,可以使用以下命令运行测试:

instruments -t "/Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.xrplugin" -D /path/to/output.xcresult /path/to/MyApp.app -e UIASCRIPT /path/to/test.js -e UIARESULTSPATH /path/to/results

这里,-t 参数指定使用的测试模板为自动化测试,-D 参数指定测试结果输出路径,-e UIASCRIPT 参数指定自动化测试脚本路径,-e UIARESULTSPATH 参数指定详细测试结果的输出路径。

9. 与持续集成(CI)集成

9.1 持续集成的意义

将自动化测试与持续集成(CI)系统集成,可以在每次代码提交或合并时自动运行测试,确保代码的质量。常见的 CI 系统有 Jenkins、CircleCI、Travis CI 等。

9.2 在 Jenkins 中集成

以 Jenkins 为例,首先需要在 Jenkins 服务器上安装 Xcode 命令行工具,并配置好相关环境变量。

在 Jenkins 中创建一个新的自由风格项目,在“Build”步骤中添加执行命令行运行自动化测试的脚本。例如:

instruments -t "/Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.xrplugin" -D /path/to/output.xcresult /path/to/MyApp.app -e UIASCRIPT /path/to/test.js -e UIARESULTSPATH /path/to/results

配置好后,每次代码提交到 Jenkins 监控的代码仓库时,Jenkins 会自动拉取代码,运行自动化测试,并生成测试报告。通过这种方式,可以及时发现代码中的问题,提高应用的稳定性和质量。

通过以上对 Objective - C 中 UIAutomation 与自动化测试的详细介绍,我们可以看到它为 iOS 应用开发提供了强大的测试能力,从简单的元素操作到复杂的多场景和数据驱动测试,再到与持续集成的集成,都能够有效地保障应用的质量和稳定性。