ios ui自动化测试_改善ios ui测试

ios ui自动化测试

Expedia Group Technology —软件(EXPEDIA GROUP TECHNOLOGY — SOFTWARE)

介绍(Introduction)

In 2018 we started writing user interface (UI) tests for the Vrbo™️ iOS app. UI tests are incredibly valuable, but also very different to write and maintain in comparison to unit tests. If your code isn’t structured well to support UI tests, and your contributors aren’t educated on best practices, they can quickly become unwieldy. This was all too evident at Vrbo, where after just a couple of years our iOS UI tests were a broken mess:

在2018年,我们开始为Vrbo™️iOS应用编写用户界面(UI)测试。 UI测试非常有价值,但是与单元测试相比,UI测试的编写和维护也大不相同。 如果您的代码结构不佳,无法支持UI测试,并且您的贡献者没有受过最佳实践的教育,那么他们很快就会变得笨拙。 这在Vrbo上非常明显,在短短几年后,我们的iOS UI测试就一团糟了:

  • 64% of UI tests consistently failed, or 100 out of 157.

    64%的UI测试始终失败,或157个测试中有100个失败

  • On average, it took 32.7s to run each UI test, or 85.5min to run all 157 UI tests.

    平均而言,运行每个UI测试需要32.7秒,或者 85.5分钟运行所有157个UI测试。

  • On average, it took 381s to perform a clean build of our UITests scheme and 44s for a follow-up build.

    平均而言,执行UITests方案的完整构建需要381秒,而后续构建则需要44s

By establishing better software patterns, leveraging 3rd party tools, and gaining a deeper understanding of Apple APIs, we’ve built a new UI testing approach that is faster, more stable, and easier to comprehend.

通过建立更好的软件模式,利用第三方工具以及对Apple API的更深入了解,我们构建了一种新的UI测试方法,该方法更快,更稳定且更易于理解。

  • All UI tests consistently pass.

    所有UI测试均始终通过

  • On average, it now takes 14.7s to run each UI test, or 353s to run 24 UI tests. This is 55% faster than before and without being parallelized yet, so they will only get faster!

    平均而言,现在每个UI测试需要14.7s来运行,而运行24个UI测试则需要353s这比以前快了55% 尚未并行化,因此它们只会变得更快!

  • Most UI tests now live within our experience modules: Xcode projects that encapsulate an isolated part of our app’s user experience and generate a framework for our top level project to consume. In these smaller, more isolated projects, it only takes 170s to do a clean build, and 25s for a follow-up build. This is 55% faster clean build times, and 43% faster follow-up build times.

    现在,大多数UI测试都存在于我们的体验模块中 Xcode项目封装了应用程序用户体验的孤立部分,并生成了供顶层项目使用的框架。 在这些更小,更孤立的项目中,只需要170秒钟即可完成一次干净的构建,而后续的构建则只需25秒钟。 这将使干净构建时间缩短55%,将后续构建时间缩短43%。

Our initial approach was certainly flawed, but having the ability to write fast, stable UI tests is critical to scaling native apps at Vrbo, so let’s take a closer look at UI testing on iOS, what went wrong, and how we can do it better.

我们最初的方法肯定存在缺陷,但是能够编写快速,稳定的UI测试对于在Vrbo上扩展本机应用至关重要,因此让我们仔细研究一下iOS上的UI测试,出了什么问题以及如何做得更好。

Image for post

什么是UI测试? (What is UI Testing?)

Unlike unit tests which verify the correctness of business logic, UI tests verify the user interface. These tests interact with an app’s UI in the same way a user does, without access to internal functions and variables. There are two processes involved when UI testing on iOS: a test runner and the app being tested. XCUIApplication is a proxy for the app, and UI tests call XCTest APIs to tell the test runner how to interact with the app’s UI. Verifying an app’s functionality through the lens of a user is powerful, and that’s exactly what UI tests do. Like unit tests, they increase code coverage and lead to more stable releases, but they also give you a level of confidence that unit tests can’t because they actually tap on your buttons, navigate between your views, and verify the content in the same way your users will. After all, users don’t care if your functions work, they care if your app works.

与测试业务逻辑正确性的单元测试不同,UI测试验证用户界面。 这些测试以与用户相同的方式与应用程序的UI交互,而无需访问内部函数和变量。 在iOS上进行UI测试时涉及两个过程:测试运行程序和被测试的应用程序。 XCUIApplication是应用程序的代理,UI测试调用XCTest API来告诉测试运行者如何与应用程序的UI交互。 通过用户的角度验证应用程序的功能非常强大,而这正是UI测试所要做的。 像单元测试一样,它们可以增加代码覆盖范围并带来更稳定的版本,但是它们也使您对单元测试无法做到有信心,因为它们实际上是敲击您的按钮,在视图之间导航并验证相同内容。用户的方式。 毕竟,用户不在乎您的功能是否正常工作,而在乎您的应用程序是否正常工作。

为什么要编写UI测试? (Why Write UI Tests?)

Unit tests can verify the UI too, so if UI tests are slower, less stable, and more difficult to write, why even bother? I hear this come up all the time, and it’s a valid question, so let’s discuss it. It’s true that unit tests can create views and reference code in those objects to verify code paths that are executed when a user interacts with your UI, but they are testing these code paths in isolation. UI tests actually simulate a user interacting with your app in real time. So while a unit test can verify that func buttonTapped() behaves correctly when called, a UI test can verify that clicking that button behaves correctly in real time, when the app is running in the hands of your users. If the connection between the button and buttonTapped() function wasn’t set, the unit test wouldn’t notice, and that’s a bug. Unit tests also don’t setup a root Window or have dedicated APIs for testing the UI, so trying to do so is usually clunky. The biggest advantage to verifying your UI within unit tests is speed, so much so that popular iOS UI testing frameworks like KIF do exactly that. That said, writing traditional UI tests (using Apple’s UI Testing Bundle) has advantages:

单元测试也可以验证UI,因此,如果UI测试速度较慢,稳定性较差且编写起来更加困难,那么为什么还要麻烦呢? 我一直都听到这个消息,这是一个有效的问题,所以让我们讨论一下。 确实,单元测试可以在这些对象中创建视图和参考代码,以验证当用户与UI交互时执行的代码路径,但是它们正在隔离地测试这些代码路径。 UI测试实际上模拟了用户与您的应用程序实时交互。 因此,虽然单元测试可以验证func buttonTapped()在调用时的行为正确,但是UI测试可以在应用程序在用户手中运行时,确保单击该按钮的行为实时正确。 如果未设置button和buttonTapped()函数之间的连接,则不会注意到单元测试,这是一个错误。 单元测试也没有设置根窗口或没有专用的API来测试UI,因此尝试这样做通常很麻烦。 在单元测试中验证UI的最大优势在于速度,以至于如此流行的iOS UI测试框架(如KIF)就可以做到这一点。 也就是说,编写传统的UI测试(使用Apple的UI测试包)具有以下优点:

  • You can record interactions to generate UI testing code. It’s far from perfect, but it can give you a good idea of how to get started.

    您可以记录交互以生成UI测试代码。 它远非完美,但可以为您入门提供一个好主意。
  • Referencing Apple’s UI Testing APIs makes interacting with the UI much easier than from a unit test.

    引用Apple的UI测试API使与UI的交互比进行单元测试要容易得多。

  • UI tests verify your code through the lens of a user, automating the verification of user flows in a way unit tests can never do because they only test in isolation.

    UI测试通过用户的角度来验证您的代码,从而以一种单元测试无法做到的方式自动验证用户流,因为它们只是独立进行测试。
Image for post

2018年的UI测试-回顾 (UI Testing in 2018 — A Retrospective)

When UI tests were first introduced at Vrbo in 2018 the goal was to automate the verification of common user flows to alleviate the workload on our QA engineers. We built a workflow on Jenkins to run them nightly since we were uncertain if their stability and speed would degrade our PR builds. This turned out to be true, but was also their ultimate demise. The nightly build wasn’t watched closely, and developers rarely ran UI tests locally before pushing up a PR since they took over an hour to run. As a result, PRs commonly broke UI tests unintentionally. We would eventually recognize the breakage and fix it, but over time, as the app grew along with the number of contributors, our UI tests degraded significantly. Let’s look at the key issues that plagued our initial UI testing approach:

当UI测试于2018年在Vrbo上首次引入时,其目标是使常见用户流的验证自动化,以减轻我们质量检查工程师的工作量。 我们在詹金斯(Jenkins)上构建了一个工作流程,使其每晚运行一次,因为我们不确定它们的稳定性和速度是否会降低我们的PR版本。 事实证明这是事实,但这也是他们的最终灭亡。 夜间构建并未受到密切关注,并且开发人员在提高PR之前很少在本地运行UI测试,因为他们花了一个多小时才运行。 结果,PR通常无意间破坏了UI测试。 我们最终会发现损坏并进行修复,但是随着时间的流逝,随着应用程序和贡献者数量的增长,我们的UI测试大大降低了。 让我们看一下困扰我们最初的UI测试方法的关键问题:

持续集成和持续交付(CI / CD) (Continuous Integration and Continuous Delivery (CI/CD))

  • UI tests were never added to a CI/CD process tied to PR builds (because they were too slow and unstable). Without a feedback loop in place to notify contributors that their PR broke a UI test, code was commonly merged into master that broke UI tests.

    UI测试从未添加到与PR构建相关的CI / CD流程中(因为它们太慢且不稳定)。 如果没有反馈循环来通知参与者其PR违反了UI测试,则代码通常会合并到破坏UI测试的master中。

稳定性 (Stability)

  • UI tests ran against Stage endpoints meaning their stability was always tied to the stability of those endpoints. We needed a way to mock the data, but UI tests don’t have access to internal functions and properties so they can’t easily pass around mock objects like unit tests.

    UI测试针对Stage端点进行,这意味着它们的稳定性始终与这些端点的稳定性相关。 我们需要一种模拟数据的方法,但是UI测试无法访问内部函数和属性,因此它们无法轻松地像单元测试那样传递模拟对象。
  • When writing UI tests, developers commonly forgot to wait on the existence of a UI element before interacting with it.

    在编写UI测试时,开发人员通常会忘记等待UI元素的存在,然后再与之交互。
  • Running tests on CI/CD is slower than running them locally, and some code paths are not synchronous. Contributors need to understand what code paths their test executes and choose appropriate timeouts when waiting on the existence of UI elements before interacting with them.

    在CI / CD上运行测试比在本地运行测试慢,并且某些代码路径不同步。 贡献者需要了解其测试执行的代码路径,并在与UI元素交互之前等待UI元素的存在时选择适当的超时。
  • Sometimes tests just fail. This happens more frequently with UI tests compared to unit tests due to the asynchronous nature of having a test runner interact with your app’s UI. Having a solution in place to retry failed tests can dramatically reduce the number of false negatives. We had no such solution in place.

    有时测试只是失败。 与单元测试相比,与单元测试相比,这种情况发生得更频繁,这是因为让测试运行程序与应用程序的UI交互具有异步特性。 拥有适当的解决方案以重试失败的测试可以大大减少误报的数量。 我们没有适当的解决方案。

速度 (Speed)

  • Poorly written assertions can force UI tests to wait for long timeouts to expire before continuing to execute a test. Many cases like this existed in our original UI tests.

    写得不好的断言可能会迫使UI测试在继续执行测试之前等待较长的超时时间到期。 我们原始的UI测试中存在许多类似的情况。
  • UI tests are inherently slower than unit tests. There are two processes instead of one, and between each test run the app has to be torn down and launched again. Taking actions on an app’s UI is also much slower than executing code line by line as a unit test does. Due to the inherently slow nature of UI tests, they often need to be parallelized. Ours were not.

    UI测试本质上比单元测试慢。 有两个过程而不是一个过程,并且在每次测试运行之间,必须将应用程序拆除并重新启动。 与在单元测试中逐行执行代码相比,在应用程序的UI上执行操作也要慢得多。 由于UI测试固有的缓慢特性,通常需要并行化它们。 我们的不是。
  • Our UITests scheme was slow to compile which in turn made writing UI tests a slow process. We modularized our codebase, but never setup our modules to support UI testing against their demos in a more isolated environment. This meant all UI tests continued to run against our top level project and never gained the compile time benefits of modularization.

    我们的UITests方案编译速度很慢,从而使编写UI测试的过程很慢。 我们对代码库进行了模块化,但从未设置模块来支持在更隔离的环境中针对其演示进行UI测试。 这意味着所有UI测试都继续针对我们的顶级项目运行,而从未获得模块化带来的编译时收益。

代码结构和易于理解 (Code Structure and Ease of Comprehension)

  • Most of our UI testing code was undocumented.

    我们的大多数UI测试代码均未记录。
  • There was a lack of internal documentation to educate contributors on best practices and debugging techniques when writing UI tests.

    缺少内部文档,无法在编写UI测试时向贡献者提供最佳实践和调试技术的教育。
  • There was a lack of SOLID software patterns in place to better structure our UI testing code.

    缺少SOLID软件模式来更好地构建我们的UI测试代码。
Image for post

UI测试和CI / CD (UI Tests and CI/CD)

There’s not much to say here. UI tests need to be part of CI/CD just like unit tests. They need to be run on PR builds otherwise contributors won’t know if they broke something. The key is really ensuring that your UI tests are stable and fast enough to be run on PR builds without making that process a major pain point for contributors, so let’s focus on those topics.

这里没有太多要说的。 就像单元测试一样,UI测试也必须是CI / CD的一部分。 它们需要在PR版本上运行,否则贡献者将不知道他们是否破坏了某些内容。 关键在于确保您的UI测试足够稳定且足够快,可以在PR构建中运行,而又不会使该过程成为贡献者的主要痛点,所以让我们关注这些主题。

编写稳定的UI测试 (Writing Stable UI Tests)

存根网络请求(Stub Network Requests)

Having UI tests hit live endpoints can be a useful tactic for writing end-to-end (E2E) tests that verify backend services in addition to the UI, but these tests are hard to automate as part of PR builds because their stability is always tied to the stability of the endpoints being hit. We wanted the flexibility to continue writing these kinds of E2E tests, while also having the option to stub network requests so we could write more stable UI tests that run on PR builds. Enter Swifter!

使UI测试到达实时端点可能是编写端到端(E2E)测试以验证除UI之外的后端服务的有用策略,但是这些测试很难作为PR构建的一部分进行自动化,因为它们的稳定性始终受到限制达到被击中端点的稳定性。 我们希望能够继续编写此类E2E测试的灵活性,同时还可以选择对网络请求进行存根处理,以便我们可以编写在PR版本上运行的更稳定的UI测试。 输入Swifter

Swifter is a tiny HTTP server engine that runs on a local port and synchronizes network requests so we can stub them while testing. We wrote a MockServer class that wraps around Swifter and exposes APIs for easily stubbing network requests. The end result is code like this:

Swifter是一个微型HTTP服务器引擎,它在本地端口上运行并同步网络请求,因此我们可以在测试时对它们进行存根。 我们编写了一个MockServer类,该类包装了Swifter,并公开了API以便轻松处理网络请求。 最终结果是这样的代码:

/// Tests happy path for logging into the app.
func testLogin() {
let stubs = [
HTTPStubInfo(urlPath: "/path/to/authEndpoint", filename: "authenticate_success", method: .POST),
HTTPStubInfo(urlPath: "/path/to/pull/profile/data", filename: "userProfile_success", method: .POST)
]
stubs.forEach { server.addJSONStub($0) }

login(emailAddress: "foo@gmail.com", password: "password")
}

In this example, we create stubs for authentication and profile requests, tell our MockServer instance to add those stubs, then call a helper function that navigates to our login view, enters an email and password, and attempts to login.

在此示例中,我们为身份验证和配置文件请求创建存根,告诉我们的MockServer实例添加这些存根,然后调用帮助程序功能,该功能导航到我们的登录视图,输入电子邮件和密码,然后尝试登录。

等待存在 (Wait For Existence)

When writing UI tests, you should always call waitForExistence(timeout:) before interacting with a UI element.

编写UI测试时,应始终在与UI元素交互之前调用waitForExistence(timeout :)

Consider the code below which grabs a reference to a button, but does not wait on that element to exist before interacting with it. If we have just navigated to a view containing this button, the test runner may attempt to tap the button before the view has even appeared on the screen. In this case, the test will fail on line 2 because the button does not exist yet.

考虑下面的代码,该代码获取对按钮的引用,但在与该元素进行交互之前不等待该元素存在。 如果我们刚刚导航到包含此按钮的视图,则测试运行程序可能会尝试在该视图甚至尚未出现在屏幕上之前点击该按钮。 在这种情况下,测试将在第2行失败,因为该按钮尚不存在。

let button = app.buttons["SomeButton"]
button.tap()

Instead, we need to wait for the button to exist before interacting with it. The code below will give the test runner 5 seconds to wait for the button to exist. From a performance standpoint, it’s worth noting that the test runner will not wait for that entire timeout duration to expire before checking its existence. Instead, it will check at regular intervals (in this case, about every second). If the check succeeds, it will continue executing and tap the button, otherwise the timeout will be reached and the test will fail on line 2.

相反,我们需要等待按钮存在才能与之交互。 下面的代码将使测试运行器有5秒钟的时间等待按钮存在。 从性能的角度来看,值得注意的是,测试运行程序在检查其存在之前不会等待整个超时时间到期。 相反,它将定期检查(在这种情况下,大约每秒检查一次)。 如果检查成功,它将继续执行并点击按钮,否则将达到超时并且测试将在第2行失败。

let button = app.buttons["SomeButton"]
XCTAssertTrue(button.waitForExistence(timeout: 5.0))
button.tap()

使用适当的超时 (Use Appropriate Timeouts)

Building on the point above, we need to use appropriate timeouts when calling waitForExistence(timeout:). We wrote a struct called UITestTimeout for this exact purpose that defines timeout cases for common testing scenarios. The cases are well documented so contributors can more easily determine which case is best to use based on the user flow they are testing, and using these cases makes it easy for us to change all of our timeouts from a single location because there are no magic numbers floating around our UI tests using custom timeout durations.

基于以上几点,我们在调用waitForExistence(timeout :)时需要使用适当的超时。 为此,我们编写了一个名为UITestTimeout的结构,该结构定义了常见测试场景的超时情况。 这些案例都有据可查的文档,因此贡献者可以根据他们正在测试的用户流轻松地确定哪种案例最适合使用,使用这些案例使我们可以轻松地从一个位置更改所有超时,因为这没有魔术使用自定义超时时间在我们的UI测试中浮动的数字。

The reason timeout usage is so important is because UI tests will always run slower on remote build machines during CI/CD builds compared to local test runs, some code paths are asynchronous, and some code paths have animation or debounce timers associated with them. These scenarios require different timeout durations, so UITestTimeout was created with them in mind. Each case has a timeout longer than would typically be required to run UI tests locally so they are more stable when running on CI. This does not impact testing speed so long as our waitForExistence(timeout:) calls are embedded in XCTAssertTrue() statements (more on that later).

超时用法之所以如此重要的原因是,与本地测试运行相比,CI / CD构建期间UI测试在远程构建计算机上的运行总是较慢,某些代码路径是异步的,并且某些代码路径具有与之关联的动画或反跳计时器。 这些方案需要不同的超时时间,因此在创建UITestTimeout时要牢记它们。 每种情况下的超时时间都比在本地运行UI测试通常所需的超时时间长,因此在CI上运行时它们更加稳定。 只要我们的waitForExistence(timeout :)调用被嵌入XCTAssertTrue()语句中,这就不会影响测试速度。

重试逻辑 (Retry Logic)

Sometimes tests fail. Asynchronous code paths can cause timeouts to be hit, simulators can crash, CI/CD can have issues, etc. Modern software is complicated, full of interconnected dependencies all expected to behave a certain way, but sometimes one breaks. We accepted this fact and began searching for a way to retry failed tests. We recently started using fastlane’s multi_scan plugin to retry failed unit tests up to a certain number of times before marking them as failed. We don’t have a retry solution in place for UI tests yet, but we are going to investigate re-using this fastlane plugin for UI tests vs leveraging Flank (a 3rd party testing framework by Google) which also provides this capability along with many other useful features.

有时测试失败。 异步代码路径可能会导致超时,模拟器崩溃,CI / CD可能出现问题等。现代软件非常复杂,充满了相互依存的依赖关系,所有这些依赖关系都以某种方式运行,但有时会中断。 我们接受了这一事实,并开始寻找重试失败测试的方法。 我们最近开始使用fastlanemulti_scan插件重试失败的单元测试达一定次数,然后再将其标记为失败。 我们还没有针对UI测试的重试解决方案,但是我们将研究如何将此Fastlane插件重用于UI测试,以及如何利用Flank (Google的第三方测试框架)来提供这种功能以及许多其他功能。其他有用的功能。

编写快速的UI测试 (Writing Fast UI Tests)

错误的断言(Bad Assertions)

Perhaps the most common mistake I’ve seen that leads to longer UI test runs is the misuse of assertions when calling waitForExistence(timeout:). When placed inside of an XCTAssertFalse(), waitForExistence(timeout:) forces the test runner to wait the entire timeout duration before checking a UI element’s existence. This differs from the behavior discussed above when wrapping a waitForExistence(timeout:) inside of an XCTAssertTrue(). This kind of check can make sense in certain circumstances, but should be avoided if possible. In other words, it’s better to check for what you expect to exist, rather than check for what you expect not to exist.

我看到的导致更长的UI测试运行时间的最常见错误可能是在调用waitForExistence(timeout :)时滥用了断言。 当放置在XCTAssertFalse()内时waitForExistence(timeout :)会强制测试运行程序在检查UI元素存在之前等待整个超时时间。 这与在XCTAssertTrue()中包装一个waitForExistence(timeout :)时,上述行为不同。 在某些情况下,这种检查很有意义,但应尽可能避免。 换句话说,最好检查一下您期望的东西,而不是检查一下您期望的东西。

并行化 (Parallelization)

As with all software, parallelization can help speed things up but has its trade-offs. Let’s look at a study done at Bitrise (our Mobile CI/CD tool) on parallelizing tests:

与所有软件一样,并行化可以帮助加快处理速度,但需要权衡取舍。 让我们看一下在Bitrise (我们的移动CI / CD工具)上进行的一项并行化测试

The impact of parallelization on build times

并行化对构建时间的影响

  • 1 Simulator: 71 minutes

    1个模拟器:71分钟
  • 2 Simulators: 43 minutes

    2个模拟器:43分钟
  • 3 Simulators: 38 minutes

    3个模拟器:38分钟
  • 4 Simulators: 34 minutes

    4个模拟器:34分钟

The impact of parallelization on build failure rates

并行化对构建失败率的影响

  • 1 Simulator: 0.0% failure rate

    1个模拟器:0.0%的失效率
  • 2 Simulators: 0.0% failure rate

    2个模拟器:0.0%的失效率
  • 3 Simulators: 1.5% failure rate

    3个模拟器:1.5%的失效率
  • 4 Simulators: 5.4% failure rate

    4个模拟器:失效率5.4%

As you increase the number of parallel simulators you trade speed for stability. The speed gains fall off as well, so there’s a balancing act. This is where having retry logic in place, like we discussed above, helps compliment parallelization. Our MockServer already supports being parallelized, so after we implement a retry solution for UI tests, we’ll spike a solution for parallelization to further reduce UI test run times.

随着并行模拟器数量的增加,您会为了稳定而牺牲速度。 速度增益也会下降,因此存在一种平衡行为。 就像我们上面讨论的那样,在这里拥有重试逻辑有助于补充并行化。 我们的MockServer已经支持并行化,因此在为UI测试实现重试解决方案之后,我们将添加并行化解决方案以进一步减少UI测试运行时间。

模块化影响编译时间 (Modularization Impacts Compile Time)

We finished modularizing the iOS Vrbo code base in early 2020, and our revamped UI testing approach now takes advantage of it. UI tests that verify cross module interactions or hit live endpoints still live in our app layer (the top level project), but now each experience module (lower level projects) supports the ability to write UI tests against its demo. Many UI tests can be written against these isolated experiences, and doing so drastically reduces compile time:

我们已于2020年初完成了iOS Vrbo代码库的模块化,现在我们经过改进的UI测试方法就可以利用它。 验证跨模块交互或命中实时端点的UI测试仍然存在于我们的应用程序层(顶级项目)中,但是现在每个体验模块(低级项目)都支持根据其演示编写UI测试的功能。 可以针对这些孤立的经验编写许多UI测试,并且这样做可以大大减少编译时间:

  • On average, clean building the app layer’s UITests scheme takes 544s vs 170s for a module’s UI test scheme. This is 69% faster clean build times when UI testing within a module.

    平均而言,干净构建应用程序层的UITests方案需要544s而不是模块的UI测试方案需要170s 。 在模块内进行UI测试时,这比原始构建时间快69%

  • On average, building the app layer’s UITests scheme takes 67s vs 25s for a module’s UI test scheme. This is 63% faster build times when UI testing within a module.

    平均而言,构建应用程序层的UITests方案需要67s而不是模块的UI测试方案需要25s 。 在模块内进行UI测试时,构建时间缩短了63%

第三方UI测试框架 (Third Party UI Testing Frameworks)

There are 3rd party UI testing frameworks like EarlGrey and KIF that swizzle Apple code and leverage undocumented APIs to increase UI testing speed. We do not use any of these frameworks currently, and I hesitate to do so. We will monitor our UI testing performance and potentially spike these solutions in the future, but our current implementation is built directly on top of Apple’s XCTest APIs, giving us the maximum amount of flexibility and control, without coupling us to an external dependency.

有第三方的UI测试框架,例如EarlGreyKIF ,它们使Apple代码混乱,并利用未公开的API来提高UI测试速度。 目前,我们不使用任何这些框架,我对此很犹豫。 我们将监视我们的UI测试性能,并在将来可能会采用这些解决方案,但是我们当前的实现直接建立在Apple的XCTest API之上,从而为我们提供了最大的灵活性和控制力,而没有使我们与外部依赖关系耦合。

编写SOLID UI测试 (Writing SOLID UI Tests)

Writing good UI testing code is hardly different from writing good software in general: Learn from the past, follow SOLID principles, and read Robert Martin’s Clean Code. I’m sure not all software engineers agree with me on that approach, but it’s my go-to! That said, let's look at exactly what we did to establish better software patterns for UI testing this time around:

编写良好的UI测试代码与编写一般的软件几乎没有什么不同:学习过去,遵循SOLID原则,并阅读Robert Martin的Clean Code 。 我敢肯定并不是所有的软件工程师都同意我的方法,但这是我的首选! 就是说,让我们来看看这次我们为UI测试建立更好的软件模式所做的确切工作:

  • UITesting - a framework that houses reusable UI testing code that can be shared between our various projects: the app, experience modules, and lower level UI frameworks.

    UITesting-一个框架,其中包含可重用的UI测试代码,这些代码可在我们的各个项目之间共享:应用程序,体验模块和较低级别的UI框架。

  • UITest - a base class that exposes common features and convenience properties to all UI test classes while also abstracting away common setup code necessary to launch and tear down the app between test runs.

    UITest-一个基类,向所有UI测试类公开通用功能和便利性,同时还抽象出了在测试运行之间启动和拆卸应用程序所需的通用设置代码。

  • Protocols - built out for each module to house reusable functions for interacting with the UI within that module (like the login(emailAddress:password:) function shown above in example code). UI test classes can conform to these protocols to gain the ability to verify user flows within those areas of the app without being exposed to functionality in other areas of the app where they have no interest.

    协议-为每个模块构建,以容纳可重用的功能以与该模块内的UI进行交互(如示例代码中上面显示的login(emailAddress:password :)函数)。 UI测试类可以符合这些协议,从而能够验证应用程序这些区域内的用户流,而不会暴露于他们不感兴趣的其他应用程序区域中的功能。

  • UITestTimeout - a struct that defines timeout durations for testing different code paths.

    UITestTimeout-一种结构,用于定义用于测试不同代码路径的超时时间。

  • Mock JSON files - for stubbing network requests with Swifter.

    模拟JSON文件-用于使用Swifter暂存网络请求。

  • CoreAccessibility - a low-level, micro-framework that houses all accessibility identifiers so they can be shared between our various projects.

    CoreAccessibility-一个低级的微框架,其中包含所有可访问性标识符,因此可以在我们的各个项目之间共享它们。

  • UITestRunner - a protocol that extends its own interface to define common UI testing behaviors necessary for our AppDelegate classes in the app and experience module demos to run UI tests.

    UITestRunner-一种协议,它扩展了自己的接口,以定义应用程序中的AppDelegate类和运行UI测试的体验模块演示所需的常见UI测试行为。

All of the new code mentioned above is fully documented. The UITesting framework has 96% code coverage. Every helper function within the UITesting protocols wraps their code within a runActivity(named:block:) call to improve readability on console and test logs. These newly established patterns not only allow us to easily write and share UI testing code between the app and experience modules, but also facilitate writing small, readable UI tests.

上面提到的所有新代码均已完整记录。 UITesting框架具有96%的代码覆盖率。 UITesting协议中的每个帮助器函数都将其代码包装在runActivity(named:block :)调用中,以提高控制台和测试日志的可读性。 这些新近建立的模式不仅使我们能够轻松在应用程序和体验模块之间编写和共享UI测试代码,而且还有助于编写小的可读性UI测试。

Image for post

结论 (Conclusion)

Only time will tell how well we’ve improved iOS UI testing at Vrbo, but the initial results are promising. We still need to implement a retry solution, and parallelize our UI tests, but those become easy follow up items now that we have a better foundation for UI testing in place. Internally, we’ve updated documentation and recorded a Tech Talk to better educate our contributors on best practices when UI testing. There may be new pain points that arise as we enter a new age of UI testing at Vrbo, after all there’s always room for improvement, but that’s why we listen and iterate.

只有时间能证明我们在Vrbo上改进了iOS UI测试的程度,但是初步结果令人鼓舞。 我们仍然需要实现重试解决方案并并行化UI测试,但是由于我们为UI测试奠定了更好的基础,因此这些变得很容易跟进。 在内部,我们更新了文档并记录了技术讲座,以更好地教育用户在进行UI测试时的最佳实践。 当我们进入Vrbo的UI测试新时代时,可能会出现新的痛点,毕竟总有改进的余地,但这就是我们倾听和迭代的原因。

Learn more about technology at Expedia Group

在Expedia Group上了解有关技术的更多信息

翻译自: https://medium.com/expedia-group-tech/improving-ios-ui-testing-ad55470825b4

ios ui自动化测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值