构建 XCTest UI 测试套件的最佳实践是啥?
Posted
技术标签:
【中文标题】构建 XCTest UI 测试套件的最佳实践是啥?【英文标题】:What are the best practices for structuring an XCTest UI Test Suite?构建 XCTest UI 测试套件的最佳实践是什么? 【发布时间】:2019-03-27 16:58:17 【问题描述】:我正在为 ios 应用设置测试套件。我正在使用 Xcode 的 XCTest 框架。这只是一个 UI 测试套件。现在我有一个带有一个文件 TestAppUITests 的测试目标。我所有的测试用例都驻留在这个文件中(第一个问题:我的所有测试用例都应该在这里还是我需要更多的文件?) 在这个文件中,我有一堆测试用例,就好像它是使用该应用程序的用户一样工作。登录/创建帐户然后登录 -> 浏览应用程序 -> 检查是否加载了 UI 元素 -> 添加额外的安全性,如辅助电子邮件地址 -> 注销。 这些应该如何订购?
我进行了全方位的研究,在这里和那里发现了一些宝石,但仍有疑问。在测试目标中,您应该有多个文件吗?我的 UITest 目标中只有一个。 我的另一个大问题有点难以解释。每次我们从一开始运行测试套件时,应用程序都会在您未登录的状态下启动,因此,例如,为了测试诸如在应用程序内导航之类的内容,我需要先运行该测试以登录。现在我已经设置好了,登录测试运行一次,然后是所有其他测试,然后以注销结束。但是这个文件 TestAppUITests 变得很长,有大量的测试用例。这是最佳做法吗?
【问题讨论】:
【参考方案1】:苏。让我们把它分成更多的部分:
1/Should all of my test cases be in here or do I need more files?
嗯 - 你的测试与任何其他应用程序代码相同,你有。您是否将所有应用程序代码都放在一个文件中?可能不是,所以一个好的做法是将您的测试分成更多的类(我以他们测试的方式进行 - LoginTests
类,UserProfileTests
类等)。
更进一步 - 我将测试类和方法分成单独的文件 - f.e.我有一个方法,可以在 UI 测试中进行登录,所以我有 UITestCase+Login
扩展中的方法(UITestCase
是一个类,由所有这些 UITestCase+Something
扩展扩展)并且我有测试,即将在LoginTests
中进行登录,我从UITestCase+Login
扩展名中调用登录方法。
但是 - 您不一定需要更多的测试类 - 如果您决定将所有 UI 测试放在一个类中,那是您的选择。一切都会好起来的,但是将这些测试使用的测试和方法放在单独的文件中只是未来开发测试和方法的好习惯。
2/... Add additional security like secondary email address... How should these be ordered?
将它们排序为方法并在测试中调用它们。
当我使用无效的登录凭据时,这是我期待一些 UI 消息的方法:
func expectInvalidCredentialsErrorMessageAfterLoggingWithInvalidBIDCredentials()
let alertText = app.alerts.staticTexts[localizedString("mobile.account.invalid_auth_credentials")]
let okButton = app.alerts.buttons[localizedString("common.ok")]
wait(
until: alertText.exists && okButton.existsAndHittable,
timeout: 15,
or: .fail(message: "Alert text/button are not visible.")
)
okButton.tap()
这是我在测试中的使用:
func testTryToLoginWitMissingBIDAndExpectError()
let inputs = BIDLoginInputs(
BID: "",
email: "someemail@someemail.com",
departureIATA: "xxx",
dayOfDeparture: "xx",
monthOfDeparture: "xxx",
yearOfDeparture: "xxx"
)
loginWithBIDFromProfile(inputs: inputs)
expectInvalidCredentialsErrorMessageAfterLoggingWithInvalidBIDCredentials()
您可以看到,测试非常易读,并且它们(几乎完全)由方法组成,可以在更多测试中重复使用。
3/Within the test target, should you have multiple files?
再说一遍 - 这取决于您,但是将所有内容都放在一个文件中并不适合这些测试的维护和未来开发。
4/... Each time we run the test suite from the beginning the app starts in a state where you are not logged in...Right now I have it setup so that the login test runs once, then all other tests after it, then ends with logout...
不是一个好的方法(以我的卑微观点) - 将功能放入方法中(是的,我在这里重复自己:-))并将测试用例划分为更多文件(理想情况下是根据它们的功能性质,通过“他们做什么”)。
希望这对您有所帮助,当我开始进行 iOS UI 测试时,我也在同样的问题上遇到了很多困难。 哦。顺便说一句——我在 medium.com 上关于使用 XCTest 进行 iOS UI 测试的高级策略和方法的文章将在几天后发布,我可以在它发布后添加一个链接——这应该会进一步帮助你。
【讨论】:
@Scott M - 我会在评论中联系你 :-) 很高兴这对你有所帮助。 谢谢你,这对你有很大帮助 关于第 4 点的一个后续问题/ - 对于你自己的测试套件,你是否在需要在应用程序中运行的每个测试之前运行登录测试一个登录状态呢?这是我的大问题。例如,检查 UI 元素是否加载到设置中,我应该在此之前运行 login() 测试吗?并在每次测试后运行 logout()? 好吧。这有点复杂。如果您将测试拆分为多个测试类,您可以使用 setUp 和 tearDown 块在每次测试之前和之后在这些块中运行您的代码。所以 - 例如 - 当用户登录时,你有一个测试类,其中包含用户配置文件的测试。你检查用户是否在 setUp 块中登录,如果他不是你登录的用户。在下一个需要的测试类中要注销的用户,您可以检查用户是否已在setUp中登录(同上),如果已登录,则将其注销。 总而言之-如果您不需要特定的电子邮件帐户,那么在每次测试之前和之后登录和注销不是一个好策略(浪费运行时间)。只需使用 setUp 中的代码检查用户是否已登录,然后将其登录或注销(取决于测试类)。就个人而言 - 我使用许多应用内帐户,所以我在每次测试之前运行注销,这些测试都有登录方法的测试(以确保我登录了正确的帐户)并且我不为类运行注销,这样做不依赖于登录状态。 谢谢,这给了我一些很好的思考。我今天已经取得了很大的进步。【参考方案2】:呼应this answer,将复杂应用程序的所有测试存储在单个文件中是违反最佳实践的,并且您的测试应该结构化,以便它们彼此独立,只测试每个中的单个行为测试。
将所有内容拆分为许多测试,这些测试需要在每次测试开始时重新启动您的应用程序似乎违反直觉,但这会使您的测试套件更可靠,并且更容易调试,因为未知数的数量通过更小、更短的测试来最小化测试中的问题。由于 UI 测试需要相对较长的时间来运行,这可能会令人沮丧,因此您应该通过确保您的应用具有良好的单元/集成测试覆盖率来尽量减少所需的量。
关于构建 XCTest UI 测试的最佳实践,您应该研究剧本或页面对象模型。页面对象模型已经存在了很长时间,并且有很多关于它的帖子,尽管很多帖子都倾向于关注 Selenium 或基于 Java 的测试框架。我已经使用 Swift 和 XCTest 在Page Object model 和Screenplay model 上写过帖子,它们应该对你有所帮助。
【讨论】:
以上是关于构建 XCTest UI 测试套件的最佳实践是啥?的主要内容,如果未能解决你的问题,请参考以下文章
为 AWS Device Farm 构建 XCTest UI 测试
AWS Device Farm 的 XCTest 单元和 UI 测试