Tag: uitest

如何设置Bitrise CI以运行iOS的自动Appium和Cucumber UI测试

为什么要堆叠? 在codequest中,一段时间以来,我们一直在使用Bitrise作为iOS应用程序的CI / CD工具。 最近,我们的团队不断壮大,并且我们已经获得了一个最有价值的新成员–质量检查工程师,他掌握了用Appium和Cucumber.rb(Ruby版本)编写UI测试的知识。 问题是–如何在Bitrise上进行全部设置,目前在这里只有自动化的单元测试运行并测试应用程序部署。 就像我们一样,大多数阅读此书的人可能正在寻求帮助。 令我们惊讶的是,即使是谷歌搜索也无法帮助我们找到逐步的指导。 因此,我将不涉及很多细节,也不用追逐。 我们的环境 下一步将需要一些细节。 可以这样说: 独角兽–这是我们的项目名称 Unicorn.xcworkspace –项目的工作区(我们使用Pods) 独角兽–要构建的Xcode方案名称 Unicorn.app –由构建创建的二进制文件的名称,可以在Xcode中检查(选择目标)➞构建设置➞产品名称 com.codequest.Unicorn –我们的应用程序捆绑包ID AppiumUITests / Unicorn –在git存储库中的此路径下,我们保留UI测试 我们不在git仓库中保留任何.app文件 在Bitrise中创建UI测试步骤 1.通过单击+工作流程按钮并设置名称来创建新的工作流程。

使用解析器组合器在Swift中进行高级UI测试

我一直喜欢Cucumber风格在iOS上进行UI测试。 但是我真的不喜欢在计算机上管理15000个红宝石版本,更不用说确保团队运行相同的版本,gem(我在看着你calabash-ios )等等。 我也希望它简单:只需读取代码,然后按“运行测试”即可。 因此,我决定在Swift中编写一个简单的Gherkin解析器。 我希望测试的结构如下: 方案:导入按钮起作用 鉴于我在主屏幕上 当我点击“导入”时 然后打开导入屏幕 func I_am_on_the_home_screen(){ … } func the_import_screen_opens(){ … } 该方案位于最上方,执行功能位于其下方。 请注意,没有“ I_tap_Import”功能,我希望tap和type具有足够的通用性,以使我的框架能够理解它。 解析中 POC版本基本上是在进行字符串比较和模式匹配,但是您可以想象,这不是非常有效和灵活。 对于“真实”版本,我使用了解析器组合器。 观看Swift讨论解析数学表达式的视频时,解析器组合器引起了我极大的兴趣。 联机有一些解析器组合器的介绍,因此在这里我不会做太多详细介绍,但是它的大致工作原理是这样的:基本解析器(例如,字符)使用…悬念…组合器组合在一起! 创建更复杂的解析器( 字符串解析器= n × 字符解析器),并且可以应用一些逻辑来创建语法(字符串是直到行尾的每个字符) 我花了几天的时间来“获取”它,但是一旦我将所有内容放到白板上,一切都变得有意义。 简而言之,它解析场景时忽略了前导和尾随空格。 当有一个“ tap ”关键字时,它将使用加引号的字符串作为参数来调用预建的tap函数(例如, Given I tap on “continue”调用app.buttons[“continue”].tap() 当有’ type ‘关键字时,它将使用加引号的字符串作为参数来调用预构建类型函数(例如, Given I type “hello” in field “id”调用app.textFields[“id”].typeText(“hello”) )) 如果没有关键字,它将调用带下划线的分隔线命名函数(例如, Then I […]

iOS上的UITest轻松完成

我们知道,设计和编写测试并不是世界上最令人兴奋的事情,但是对于您编写的任何应用程序,测试都是绝对必要的。 它们可能是AppStore上闪闪发光的5星级应用程序,或者是漏洞百出的代码集之间的区别。 我们都知道什么是单元测试以及如何编写它们,但是自XCode 7以来,Apple便向我们介绍了其IDE中的UI测试。 通过这些测试,您可以记录用户与应用程序的交互,并检查其是否表现正常。 在本文中,我们将重点关注测试的UI方面,以及如何在iOS应用中记录和编写您的第一个测试。 但是为什么要专注于UI? 您的应用程序可能在后台执行了它需要做的所有事情,并且单元测试可能具有100%的覆盖率,但是对于普通用户来说,如果有什么地方不合适,那么就会出现问题。 通过测试应用程序的用户界面,您可以查看用户与用户界面的交互方式,并检查他们是否看到了应有的样子。 现在,让我们深入研究如何轻松测试UI而不会有任何麻烦。 在开发iOS应用程序时,我们必须首先决定将使用哪种架构。 苹果建议 ,大多数开发人员都使用MVC,但是MVC有其缺点。 我们可能都在某个地方读过或听到过大量的视图控制器笑话 。 可悲的是,在大多数情况下是这样。 当我们不得不将与View或Model逻辑不相关的所有内容放到Controller逻辑中时,控制器中会生成很多代码,而在Model或View中则不会那么多。 让我们快速看一下Viper是什么及其组件。 如果您不熟悉Viper,让我们快速介绍一下。 Viper是View,Interactor,Pentent,Entity和R external的首字母缩写。 这基本上是一种实施“ 单一责任原则”的方法 ,旨在创建一个更清洁,更模块化的结构。 Viper通过具有以下结构来实现此目的: 在上面的插图中,我们可以看到Viper方法以及架构的每一部分如何相互集成。 毒蛇的每个元素都负责完成一项工作,并要求/将任何其他工作发送/发送到负责任的元素。 例如,演示者告诉视图它需要显示什么。 在这种情况下,View的唯一工作就是显示演示者告诉其显示的内容。 为了帮助测试,Viper的每个元素(实体除外)都实现了一个协议,因此您可以轻松模拟类的任何部分并编写零麻烦的测试。 很简单吧? 让我们深入了解Viper的每个主要组件。 视图是被动的,因为它基本上只是等待演示者提供其内容来显示。 它还需要接收来自用户的输入,如果需要处理任何内容,则将其传递给Presenter。 View还具有有关如何显示数据的所有逻辑,例如哪些信息进入UILabel,以及哪些UIImageView将用于显示Presenter发送的图像。 输入想要用于测试目标的名称并创建它。 XCode会将这个新目标添加到您的项目中,并自动创建一个与目标名称相同的新文件。 在此文件中,您将已经创建了一些功能。 在每个测试之前和之后都有setUp()和tearDown()运行,还有一个testExample()。 您可以根据需要删除它们,但是为了简单起见,您可以选择函数testExample()并开始测试。 然后,XCode将允许您通过页面底部的红色圆圈按钮记录测试。 单击它,XCode将构建您的应用程序并启动模拟器。 您在此模拟器上执行的每项操作都将记录并记录在所选功能上。 我为本文创建的页面包含两个元素:UIImage和UIButton。 当我运行记录器并单击两者时,它生成了以下代码: 哇,这是很多用于点击图像的代码,不是吗? UIButton更好,因为它具有文本并且XCode知道如何搜索它,这意味着您可以找到所需的内容。 那么,如何处理图像呢? 嗯,有一个accessibilityLabel可以帮助您在屏幕上找到任何元素。 我将图像的acessibilityLabel设置为profileImage ,这使您的测试代码更易于编写。 开发iOS应用程序时,可访问性标签是您最好的朋友。 它为听力障碍的用户提供了一种导航您的应用程序的方式,还可以帮助您更好地测试UI。 您必须问自己,为什么我首先向您介绍了Viper。 当然,我们所做的测试可以使用MVC进行,但是如果您必须测试应用程序中更深层次的内容怎么办? […]

快照XCUI测试

本教程向您展示如何将流行的测试库iOSSnapshotTestCase与XCUITests一起使用。 如果您已经知道如何创建项目并安装一些Pod,请随时跳过。 设置项目 然后在项目的根目录中,运行pod init 。 这将在项目的根目录中创建一个Podfile。 打开它,然后将pod ‘iOSSnapshotTestCase’添加到Test和UITest目标,如下所示: 然后运行pod install 。 这会将所需的文件添加到这些目标。 接下来,您将要编辑方案以添加必要的环境变量。 您可以从iOSSnapshotTestCase存储库中复制粘贴的前两个,除路径的最后一部分外,第三个将相同。 FB_REFERENCE_IMAGE_DIR是该工具将存储参考图像的位置。 该工具将在IMAGE_DIFF_DIR中存储差异以检查故障。 FAILED_UI_TEST_DIR是我们的自定义目录,我们将在其中存储XCUITest故障的快照 添加一个XCUITest 现在我们需要测试。 在Main.storyboard添加带有一些文本的UILabel 。 还要添加一个UIButton ,以一种不同的感觉与新的UIViewController结合。 例如: 在预先生成的SnapshotUITestingUITests ,将生成的代码替换为: 很好 如果将recordMode切换为false(或删除该行),则下一次运行会将屏幕截图与刚拍摄的截图进行比较。 在项目目录中找到存储的图像应该没有问题。 解决状态栏问题 我将为您省去发现的旅程,只告诉您有问题。 以这种方式做事需要整个模拟器的屏幕截图。 不用担心 我们可以解决这个问题。 将新文件添加到UITest目标,并将其UIImageExtensions 。 添加以下代码。 结论: 尚未使用太多,但看起来确实很有希望。 让我知道您的想法,如果您最终使用它! 您可以在这里找到完成的项目:https://github.com/joesus/SnapshotUITesting

如何在iOS应用程序中为多个模拟器运行UITest?

本文假定您已经知道如何在Xcode中编写UITest。 现在,如何运行与彼此交互的UITest,在这种情况下,您可以为一种消息传递功能编写测试用例,该功能可以在多个模拟器中运行多个用户,并等待他们彼此聊天。 这是消息传递应用程序的理想自动UITest,对吗? 我知道,关于此的StackOverFlow问题不多。 我知道,RayWenderlich网站上也没有教程。 我知道,您可能尝试过不同的解决方案,但是其中任何一个都有一些局限性。 但是,在这里您可以找到适合我的解决方案! 为什么呢 好吧,如果您的应用程序中具有消息传递功能,则应用程序最重要的测试范围是消息传递的整个功能,不仅要确保已发送消息,还要确保将收到消息,或者如果您有群组消息,请确保群组中的所有成员都会收到消息。 您可能会想到可能的解决方案,以下是每种解决方案的缺点: 手动测试 您知道这不是一个好选择! 因为这很耗时,所以即使您的测试场景文档齐全,您也可能会有人为错误,但是测试人员可能会错过对所有案例进行测试的机会! 在一个模拟器/设备中运行两个应用程序: 首先 ,如果您要测试多个用户,则必须为要运行的每个主应用程序定义多个捆绑标识符,并且有一个测试应用程序将与其他应用程序交互。 但可以肯定的是,这不是一个漂亮的测试环境! 其次 ,在应用程序之间进行切换将非常耗时! 第三,但最重要的是,如果您不仅仅想要像行为测试这样的UITest,或者您正在使用像KIF这样的库,它们仅与UnitTest Target兼容,那么在任何这些情况下,您都必须在UnitTest Target中使用测试用例可以访问您的应用程序模块,并获得诸如KIF之类的库的支持! 但是,您将失去运行可与主应用程序交互的单独测试应用程序的功能! 甚至,您将无法在setup()函数中运行以下命令,您将获得运行时错误! XCUIApplication()。launch() 同时运行两个模拟器: 是的,这也是我认为的最佳解决方案。 但请记住,我们不仅需要UITest,而且还使用KIF编写UITest案例!

在iOS中使用地图视图运行UI测试

您应该模拟一个位置以确保可靠的测试 创建gpx文件 转到Xcode -> File -> New -> GPX File 看起来像 奥斯陆S 2017-05-31T14:55:37Z 奥斯陆S 2017-05-31T14:55:40Z gpx文件非常强大,因为它允许您指定具有不同移动速度的路线。 提供一个或多个包含纬度/经度对的航点。 如果您提供一个 航点,Xcode将模拟该特定位置。 如果您提供多个路标, Xcode将模拟访问每个航点的路线。 (可选)为每个航路点提供时间元素。 Xcode将插补运动 以每个航点之间经过的时间为基础的速度。 如果您不提供 时间元素,则Xcode将使用固定的速度。 航点必须按时间升序排列。 使用gpx文件 在应用程序目标而非UITests目标中声明gpx文件。 转到您的app scheme -> Run -> Options 转到Simulator -> Debug -> Location -> Custom Location然后选择相同的位置,以确保确定。 它不必相同,但是我看到没有Custom Location ,它在UITests中UITests 让地图= app.maps.element(boundBy:0) let谓词= NSPredicate(格式:“ label CONTAINS’City Hall’”) 让cityHall = […]

iOS中的UI测试

简介✍️ 在本文中,我们将首先解释如何使用Xcode编写UI测试,然后如何添加可视化差异,最后解释如何在Bitrise中进行配置。 为什么要测试用户界面(UI)? 对于产品发布,要测试每个用例并确保没有回归是很痛苦的。 您可能忘记测试一个流程,或者认为您的更改不会影响代码的其他部分……但是有时会这样做。 这是在Appaloosa上为我们的iOS应用程序所发生的情况,其中一些错误逐渐消失。 例如,当发布iOS 11时,我们在导航栏中添加了约束,但是此功能在以前的版本中不可用,并且该应用在iOS 10中崩溃。 它很快被发现并修复,但是警报提醒我们采取更强有力的措施来防止此类问题。 UI测试和单元测试 使用单元测试,您可以独立测试每个小代码,并专注于代码的逻辑方面。 使用UI测试,您可以模拟用户交互和用户流。 让我们以用户尝试登录您的应用程序为例。 它将在整个过程中测试用户与我们的应用程序的不同交互。 设置📚 在Xcode上转到您的项目,单击“ +”按钮(请参见下图)并添加iOS UI测试包。 还将创建一个新目标。 您还可以在Xcode上创建项目时添加它。 样例项目 这是一个示例项目。 我们要测试多个用户流,并确保它们都能按预期工作。 您可以在github上找到完整的代码。 目的是确保我们的登录页面在不同步骤中显示正确的视图。 因此,我们将需要访问页面的不同视图以评估其在测试期间的行为。 您可以通过多种方式检索应用程序的元素: 您可以使用Xcode中的“录制”按钮,它将自动生成您的代码。 您可以在视图上添加可访问性标识符,以更轻松地访问它。 您可以手动编写代码,在这种情况下,您需要知道如何访问元素。 您可以使用LLDB中的以下命令来打印可见屏幕的详细视图层次结构: (lldb)po打印(app.debugDescription) 我更喜欢这最后一种方法,代码更具可读性。 编写您的第一个UI测试 使用无效的凭据登录: 如果用户输入无效的凭据,我们希望该应用程序显示错误消息: 让我们看看它如何转换为代码。 用户想要显示他的应用程序列表: 在这里,我们被迫等待假服务器的响应。 我们需要告诉Xcode等待,直到应用程序完成了视图的渲染。 如果不这样做,则由于目标对象尚未出现,我们的测试将失败。 为了强制Xcode等待,我对XCTestCase使用了扩展名: 用户希望查看应用程序的详细信息: 从Xcode 9开始,我们不再能够比较长度超过128个字符的字符串,因此我们不得不使用谓词:

XCode UI测试指南

苹果公司的UITest套件使一些开发人员感到兴奋,而其他人则对失去的功能感到失望。 UITest的工作方式不同于开发人员所依赖的功能测试解决方案,例如KIF。 UITest无需授予您对元素本身的访问权限,而是提供了具有最少交互参数的代理元素的访问权限。 学会将单元测试和基于单元的ui测试与功能测试区分开可能是一件令人沮丧的经历,尤其是在使用仍需要解决的新框架时。 我希望以下指南可以帮助开发人员对使用UITest时所需的方法有所了解。 *本文写于2015年12月中。那时,UITest仍遇到一些错误。 很多次我的测试都失败了,第二天没有任何变化就会通过。 建议您经常清理和删除派生数据,以及不时重新启动XCode。 您可能还必须找到一些创造性的解决方法。 更多信息请参见下面的“ Gotchas”部分。 在查看文档集时,请记住,UITest同时用于iOS和OSX测试,因此可能提到了OSX特定项(例如,单击与点击)。 怎么运行的 UITest的独特之处在于它不使用项目代码进行测试,而是存在于应用程序外部 。 UITest而是查看模拟器中可用的内容,并根据发现的内容返回给我们XCUIElement的实例, XCUIElement和XCUIApplication是用于此的代理。 例如,按钮是XCUIElement类型的XCUIElementTypeButton (或者仅仅是.Button )。 这意味着您将无法访问ObjC或Swift类/对象或它们的关联属性。 您只能访问XCUIElement属性。 这也意味着@testable中没有@testable或其他类似的导入。 UITest包含一项新的“记录”功能,可用于记录操作并帮助加快测试的编写速度。 UITests可以在没有Record功能帮助的情况下编写,因此可以成为测试驱动开发的一部分,但是,在编写要测试的代码后编写测试时,最好先使用Record,然后修改结果。 这是因为在您更加熟悉某个元素之前,有时很难预测该元素将如何暴露于UITest。 它还有助于识别您可能尚未意识到的问题,例如具有相同标识符的多个项目。 XCUIApplication是用于测试的代理。 它将为每个测试启动该应用程序的新实例,从而为您提供运行测试的基础。 与XCTest一样,它具有设置和拆卸方法,您可以封装一些常用功能,例如在测试文件中编写一个函数以清除文本字段,然后可以在所需的每个后续测试中调用该文本字段。 您还可以在XCUIElement进行扩展,以在测试套件的任何部分中重用代码。 XCUIApplication启动和终止应用程序(分别启动和终止)。 您还可以使用launchEnvironment在启动时将参数传递给应用程序,并使用launchEnvironment传递环境。 在头文件/ docset中:“与NSTask不同,在启动应用程序之后修改环境[和启动参数]是合法的。这些更改不会影响当前的启动会话,但是将在下次应用程序生效时生效。发射。” UITest建立在XCTest框架的基础上,并包括对该API的新添加: 没有任何官方文档,但是Joe Masilotti根据框架标头编译了文档。 上面的项目直接链接到那些文档。 如果作者将网站关闭,我们将在Github上分发该文档集的副本(截至2015年12月8日)。 该文档集还包括XCTest框架的其余部分,而不仅仅是UITest。 标识元素-> XCUIElementQuery 元素是使用查询来标识的,最常见的是使用标识符和类型的组合。 元素是唯一的,为了使用元素,查询必须返回一个元素。 Apple提供了一些快捷查询( app.tables返回所有表的XCUIElementQuery ,而app.table[“table identifier”]将返回类型为.Table的XCUIElement ,具有指定的标识符,而长格式为app.containingType(.Table, identifier: “table identifier”) ),并且可以根据喜好和需要使用长格式或短格式。 查询链接在一起,并且变量名可以在整个链中替换: […]

Swift Localhost:使XCUITest再次出色

XCUITest的最简单的网络存根 端到端(E2E)UI测试不仅由于多个依赖关系而脆弱,而且执行起来也很慢且昂贵。 在我以前的工作中,我记得我们的组织花大量时间运行E2E测试,却由于本地客户端错误而导致测试失败,这真令人沮丧。 通过进行客户端集成测试,可以避免此类不必要的故障。 除了单元测试之外,鼓励iOS工程师编写UI自动化测试,作为完成功能所需工作的一部分。 这些测试将使用localhost模拟外部依赖关系。 我将使用此博客文章来说明使用cocoapods和XCode为现有的iOS项目设置localhost XCUITest的人多么容易。 通过4个步骤设置iOS XCUITest Localhost 这是4个简单的步骤(LAIM),用于在iOS项目中设置localhost XCUITest。 (L)本地主机 将SwiftLocalhost(https://github.com/depoon/SwiftLocalhost)安装到您的iOS项目中。 对于iOS 9,我们需要在“ Exception Domains”下将“ localhost”添加为项。 对于iOS 10及更高版本,只需添加NSAllowsLocalNetworking并将其设置为YES。 (M)模拟 创建用于准备模拟URLResponse数据的帮助程序类/函数。 在这里,我们可以指定用于测试的模拟响应文件的位置。 记录所有到达本地服务器的出站URLRequest。 对于给定的用例,我们可以使用SwiftLocalhost断言iOS应用正在按特定顺序发送特定的出站请求。 专注于模拟 工程师可以只关注模拟响应的内容,而无需知道如何启动本地Web服务器。 请记住,SwiftLocalhost的目标仅仅是实现客户端UI集成测试,而不是断言端到端的行为。 测试用例中的嘲弄 相同的XCUITest测试功能中描述了模拟响应的设置。 这有助于使测试用例更具可读性,尤其是在同一范围内可以看到测试代码和模拟响应时。 只需使用Swift 您只需要XCode和Cocoapods即可运行SwiftLocalhost。 iOS开发人员可以放心使用Swift作为应用程序开发语言和UI测试编程语言。 Cmd + U 使用SwiftLocalhost的最大优势在于其执行简单。 iOS开发人员只需按Cmd + U即可触发UI自动化测试。 因此,无需使用bash / shell来启动本地Web服务器。 此外,连续集成作业仅需要选择XCUITest方案即可运行测试。 是的,就是这么简单。 展示柜 为了展示在现有的iOS项目中设置SwiftLocalhost是多么容易 ,我分叉了一个我在Github上随机找到的iOS应用程序存储库项目,并应用了本文中突出显示的技术。 [派生] https://github.com/depoon/ios-movies-app [原始] https://github.com/mkamhawi/ios-movies-app […]

iOS UI测试,汗水…绕……更多汗水(第1部分:Stubbing网络)

当我试图为我的iOS项目争取到一个名为CI / CD的黄金之山时,UI Testing是我必须征服的山谷。 在那个地方,那是一次浩大,朴实而又烦人的经历。 Xcode为我提供了一种在旅途中帮助我的武器,该武器称为XCUITests。 该武器在访问,交互以及在我必须击败的测试中主张时具有很大的壮举。 但是对于存根网络来说,这就像枪战中的一把刀-它使我丧命。 反应过度? 我不这么认为,在这个山谷中,您需要隔离自己,以便一切顺利。 从真实服务器获得真实响应只会延迟测试,如果没有失败的话。 盯着空白处,我一直想知道应该怎么做……直到它撞到我身上。 OHHTTPSTUBS 我很高兴找到另一种名为OHHTTPSTUBS的武器。 OHHTTPStubs是一个旨在非常轻松地对您的网络请求进行存根的库。 —取自GitHub 当我穿越那片广阔的平原时,我高兴地哭了。 乐观地说,我跳得更快,跑得更快。 就像我找到了击败龙的武器。 我的心跳加快,然后突然停下来(我还没死,很冷)。 我面前出现了一个障碍物,它的魔力使我飞回了山谷的入口。 怎么了? 我在寻找线索,武器的背面写着一个线索。 杜德(Dude),托管测试非常棘手 我认为这是为了另一个叫做“单元测试”的山谷(这是我必须走的另一条路,但是让我们稍后再谈)。 那颗充满希望的闪烁星光突然使我失望。 但是,我仍然在绝望中寻找一种更相关的武器。 也许这不是我需要的武器,可能不是车辆还是房屋? 方便地,一个老隐士路过并丢下了一封信。 我在心里大声朗读。 亲爱的儿子, 您所需要的不是武器,车辆或房屋。 这是一个模拟服务器。 尝试一些事,例如迅速或使馆 -您方便的陌生人 哦,好吧……隐士似乎不喜欢类比。 为了寻找线索,我竭尽全力击败了这个令人烦恼的山谷。 看起来很奇怪,我将尝试其中之一。 迅捷 事实证明,这个“微小的HTTP服务器”是我的解决方案,我的光明了我的黑暗,我的罗密欧是我的朱丽叶,我的托尼·斯塔克是我的奇异博士,我明白了。 遵循此页面上的任务指南,我立即迈出了第一步。 但是首先,我必须下定决心,下定决心,并确定自己的build configuration和scheme 。 使用新scheme我可以为我的应用程序提供一个环境,该环境连接到localhost:8080(Swifter将在其中托管HTTP服务器)作为该服务器的base url ,我可以得到预期的响应,我必须为每个端点提供该响应我的应用最初与之联系。 预期的响应可以与JSON文件一起提供。 锁好并装好,我准备出发了。 我可以说,第一步是“冗长但必要的”。 当我浏览端点回答日记*咳嗽* apiary *咳嗽*时,我自信地走着。 我将所有这些注释放在由JSON扩展标记的单独注释中,并将这些注释放入我的资源中。 之后,剩下的就是在公园里漫步。 […]