Tag: 工程

为什么我们使用KIF-Quick进行用户界面测试

作者:软件工程师Paul Zabelin 在DoorDash,我们的工程团队正在不断开发新代码,以改善iOS上的用户界面(UI)。 最近,我们开发了自己独特的测试系统,该系统将名为“保持功能正常”的框架与行为驱动的开发框架Quick结合在一起。 我们已经看到,我们的KIF-Quick系统提供了最好的测试结果,并自豪地宣布它是一个开源项目,因此我们鼓励其他人看看并尝试一下。 为了深入研究,出于多种原因,我们选择使用“保持功能正常”或KIF进行测试,但其中一个与众不同-可靠性。 我们需要能够相信测试是100%准确的,每次测试通过或失败。 KIF还可以快速,轻松地进行测试,这是该框架的主要吸引力。 关键是测试不会花费太多时间来运行,同时仍能提供可靠的结果。 同时,我们还使用Swift开发和编写单元测试,并使用Quick和Nimble框架提供帮助。 对于更多上下文,Quick是受RSpec启发的Swift行为驱动开发(BDD)框架。 我们之所以选择使用Quick,是因为它的组织性使其更易于阅读,这有助于简化测试代码。 使用Quick进行我们的iOS UI测试可以为工程师提供更多具有嵌套上下文的可读性规范。 另外,当在开发之外共享使用我们领域特定语言的单元测试时,我们的UI测试甚至可以从BDD样式中受益。 这就是为什么在DoorDash中我们选择将两者结合使用的原因。 KIF-Quick的易用性和可靠的结果为我们提供了UI测试的最佳体验和结果-毕竟,他们说“您的代码仅与您的测试一样好” 例子 下面比较两个用于应用登录的测试示例。 第一个示例使用KIF-Quick,另一个示例显示常规XCTest。 在Swift中使用Quick BDD语法的KIF Spec Objective-C中的KIF测试 从上面的两个示例中可以看到,第一个示例(使用KIF-Quick)提供了更具可读性的语法,这在组织多个上下文时特别有用。 如果您想了解更多信息,请在此处查看KIF-Quick,希望您发现它与我们的测试一样有益。

Buildkite和Fastlane-iOS开发人员改善睡眠的关键

在开始构建Tink应用程序的Swift版本之前,我们没有这些限制。 大多数开发人员在开始新项目时,都有他们想要实现的抱负和目标。 干净的代码。 没有警告。 良好的测试覆盖率。 主分支应始终建立。 我们实现这一目标的一种方法是建立一个持续集成和持续交付系统。 在开始对此进行讨论时,我们研究了可以使用的选项。 我们通常找到两个选项-托管和自托管解决方案。 借助托管服务(例如Circle,Bitrise),我们发现我们不得不移交对GitHub存储库的过多访问权限。 由于我们的应用程序可以处理数十万客户的财务数据,因此安全性至关重要,我们宁愿不这样做。 在阅读我们的工程博客时,您可能会注意到,我们在安全性方面非常重要。 这是我们和我们客户的财务风险。 我们选择使用自托管解决方案。 Tink的其他团队已经将Buildkite用于其CI / CD需求,因此,很显然,从长远来看,使用相同的系统对我们也将有所帮助。 建立,使用和修复可能发生的任何问题的知识已经在建筑物中了。 我们只需要点击它。 建筑风筝 我们在办公室中发现了一个旧的15英寸MacBook Pro,它的机身凹陷,视频卡出现故障以及古老的规格。它很快成为我们的第一台构建机器。当我们将笔记本电脑升级到新的USB-C MacBook Pro时,第二台旧的MacBook Pro成为了也可用-我们的第二个构建机器。 设置我们的构建机器非常容易,只需九步。 这些处理使我们的构建在机器上运行的设置过程。 我们还有其他设置机器的步骤,例如设置File Vault,在此不再赘述。 我们的服务器机房目前容纳我们的两台构建机器。 重要的是Trigger a build on all the lanes on the new build machine.突出显示“ Trigger a build on all the lanes on the new build machine. 这样,build文件夹是由Buildkite创建的,我们可以将带有秘密的keys文件复制到其中。

为什么我要从越南搬到墨西哥

胡志明市Wizeline的iOS工程师Thien Liu,将在墨西哥瓜达拉哈拉六个月与团队合作! 是什么把你带到Wizeline? 去年,我在Dreamplex的合作空间工作,并有机会参加Wizeline主持的活动。 我一直在寻找公司的文化和愿景,几个月后我申请了Wizeline! 您喜欢成为Wizeline工程师吗? 自加入Wizeline以来,我已经从其他工程师那里学到了很多东西。 此外,我们举办了许多研讨会,技术讲座和内部活动,以分享工程知识。 Wizeline还提供诸如CodeSchool,Plural Sight等学术课程,以鼓励工程师提高其技术水平。 随时都可以享用美味佳肴和小吃也是我在这里最喜欢的地方之一! 到目前为止,您在Wizeline上最喜欢的项目是什么? 我最近参与的最喜欢的项目之一是为数字媒体客户端提供内容。 我将与瓜达拉哈拉办事处的工程师合作,为澳大利亚的这个客户提供服务。 这是一个挑战,非常有趣! 您最想知道墨西哥什么? 新朋友,当然! 我迫不及待地想见一个在一个崭新的国家里从未见过的人。 帮助架设Wizeline的办公室也是一项重要责任。 我会想念我在越南的队友,但我希望能体验并将瓜达拉哈拉最好的地方带回我们在越南的办公室。 如果您想体验越南的硅谷文化,请立即申请Wizeline! 我们有许多工程职位,而且更加开放。

重新架构Pinterest的iOS应用

加勒特·月亮| Pinterest工程师,iOS核心体验 一小组Pinterest iOS工程师最近获得了每个工程师梦dream以求的机会-完全重新思考和重建我们的应用程序。 我们发展如此之快,以至于对我们的平台提出了很高的要求。 为了覆盖全球下一个亿的Pinner,我们从头开始重新构建了平台的架构,该框架的内置速度更快,扩展更容易,并且在许多不同的情况下,Pinners的速度提高了3倍。 以下是该项目的一些较大部分,还有更多内容将在未来几周内发布。 出发吧! 表现胜出 要在美国以外的国家/地区吸引更多人,则要求该应用程序性能尽可能出色,尤其是在旧设备上。 这意味着需要解决以前可能出现的问题(例如,滚动家庭供稿时帧丢失)。 减少能耗和提高性能的最快,最简单的方法是利用大多数iPhone具有多个处理器这一事实。 我们的新系统严重依赖多线程代码。 UI渲染,图像下载,GIF解码和模型处理都是我们设计为多线程的系统的所有示例。 这使我们可以更好地保持Pinner轻触时滚动和响应的流畅性。 在测试中,我们测得触摸响应和滚动平滑度提高了10倍。 没有一堆细微的调整,什么绩效项目才能完成? 我们至少构建了三种不同的方法来圆角化视图(在CALayer上使用cornerRadius属性的成本之快令人惊讶地加起来了)。 异步显示 大多数新框架都建立在AsyncDisplayKit(ASDK)之上,AsyncDisplayKit是一个开放源代码iOS框架,最初由Pinterest工程师Scott Goodson编写,可保持UI流畅和响应。 ASDK似乎是解决我们问题的灵丹妙药,因为它提供了UI的异步呈现,因此我们可以完成布局和呈现主线程的昂贵工作。 尽管异步显示是ASDK的主要承诺,但它还提供了一个类似于CSS Box Model的布局系统,可以轻松使用CALayers而不是UIViews(不需要注册触摸时),并且还可以进行其他许多性能改进。 一种特别具有影响力的优化是滚动视图的不同范围的计算。 ASDK将在Pinner的滚动位置之前计算一个单元格范围,该单元格需要从网络中获取其数据,并需要较小范围的单元格以进行预渲染。 这将自动允许及时获取,渲染和清除用户已通过的范围,从而节省了内存和在各个运行循环上花费的时间,从而加快了滚动速度。 当然,在重写几乎整个应用程序以利用ASDK的过程中,我们发现了错误,并向项目提交了许多改进。 实际上,对ASDK的十大贡献者中有五位是Pinterest员工。 不变的数据模型 历史上,另一个重大的痛点是由我们的模型系统引起的。 我们拥有的系统是间歇性线程安全的,并且虽然可以在后台线程上创建模型,但需要对模型进行任何修改才能在主线程上进行。 这对性能不利,并且引入了开发人员并不总是显而易见的复杂性,从而导致许多崩溃。 我们需要的是一个线程安全和高性能的系统。 保证线程安全的最简单方法是使所有内容保持不变,但是要更改近100种模型并在开发新功能时对其进行更新是一个挑战。 为了解决这个问题,我们自己生成了它们。 我们的一位杰出工程师(Rahul Malik)离开了,学到了一些Swift并建立了一个将JSON文件变成不可变模型的系统。 我们对生成器感到非常兴奋,并将很快将其开源并通过博客发布! 现在,我确定您正在问自己:“但是Garrett,如果您的模型是不可变的,如何在您的主板上获得架构图钉?” 我很高兴你问! 我们新的不可变模型允许使用构建器系统创建修改后的副本。 我们有一个强大的框架(由才华横溢的Wendy Lu构建),可以快速安全地将更改通知UI和其他感兴趣的观察者。 我们很快将分享有关模型,生成器和系统的更多信息,以使UI保持同步。 新的设计语言和系统 除了几乎完全重写iOS应用程序之外,我们还承担了全面改革视觉设计的任务。 我们与产品设计团队紧密合作(包括到波特兰远程办公室的实地考察),以实现一种新的设计语言和框架,从而简化了针对多种设备尺寸的构建应用程序的工作。 与设计师一起,我们创建了BRIO的外观(内部设计代号)和要点(即“主体”)以指定设备不可知的布局。 一旦大声说出来,它就不会消失。 新框架还根据屏幕大小和语言自动缩放字体,边距和字距。 我们发现中文,日文和韩文语言对于本机读者来说显得太大了,没有一点扩展,因此我们的框架会自动处理。 总体而言,这是任何工程师都可以落后的系统:适用于任何屏幕尺寸的一种实现方式(好吧,如果IPAD分散存在,还可以##,但至少要少一个数量级)。 没有一些新的交互设计,什么视觉刷新才能完成? […]

软件工程:成瘾者的天堂

直到我大学一年级才开始开发者的旅程。 我曾在高中尝试过编程课程,但我的学校似乎并不太热衷于。 我确实参加了很多数学和科学课程,以及一些动手实践的贸易课程,例如木工和焊接。 我喜欢解决逻辑难题,玩视频和棋盘游戏,并且通常喜欢任何需要策略或批判性思维的东西。 自然,我对软件开发事业非常感兴趣。 当我上大学的第一门计算机课程时,我已经下定了决心要在本科期间完成两件事:1)获得计算机科学学位; 2)了解有关软件的所有知识。 获得学位是一个非常直接的目标,但是我显然不明白完成2分几乎是不可能的,我可以花整个职业生涯来实现这一目标。 当您决定进入基于STEM的职业时,会发生一件有趣的事情。 您可能会想,“我将获得<在这里插入工程专业的学位,然后我将完成学习。” 90秒钟内您都是正确的。 在科技行业,现实世界看起来就像我上大学时熬夜的时候一样,学习新的语言并完善我最近获得的技能。 学习所有的东西 我从学习Basic开始,然后转到Python,C,C ++和SPARC Assembly。 我自学了HTML,CSS,PHP,Javascript和SQL,因此可以找到一份兼职工作。 从那里开始,螺旋式失控。 每个学期,我都用一到两种“熟悉的”语言进行编码,并用三到四种对我来说是新的语言进行编码。 在大学三年级结束时,我已经用大约16种语言工作。 这帮助我找到了新的编码机会,但是这也使得在面试中保持所有语法的一致性非常困难。 上大学的最后一年,我已经尝试了足够多的语言,我的高级项目将有很多选择。 现在回想一下, 如果我没有花时间继续寻找新的语言来学习,那么我可能会陷入对我的高级项目和职业生涯的一条单一道路。 我觉得很多人都认为,如果您拥有计算机科学学位,那么选择一种新语言就变得微不足道了。 当然,我们学会了很多理论,并且学习了语言在幕后的实际工作方式,但是我认为实践最终使每次选择一种新语言变得更加容易。 您会开发使学习过程本身成为可能的技能和习惯,而不是始终知道要学习什么。 迅速陷入深渊 在我作为iOS开发人员的职业生涯的几年中,Swift的第一个版本发布了。 在那段时间里,我几乎一直只在Objective C中工作,没有真正接触其他语言。 斯威夫特出来时我的下意识反应是奔跑。 奔跑无处不在,并坚持使用过多的括号和混合C代码。 Swift刚问世时,我对此表示怀疑。 我看不到如何实现我想要的一切,而且我当然无法像在Objective C中那样快地工作。实际上,我大多只是害怕抛弃对Objective C的熟悉而继续前进新的和不同的。 如果我学不到该怎么办? 如果我在Swift方面不如在Objective C方面成功,该怎么办? 这能使我失去工作吗? 让我们花一些时间来谈论这些问题。 如果我学不到该怎么办? 这太荒谬了-我已经训练自己超过五年了,能够快速,成功地学习新事物。 教育的大部分好处不一定基于我所学的知识,而是基于我的学习方式。 我应该能够做出真诚的尝试来学习Swift。 如果我在Swift方面不如在Objective C方面成功,该怎么办? 这能使我失去工作吗? 简短的回答:绝对可以。 我的一部分工作是编写不会被逻辑错误困扰,易于维护且不会充满误用概念的代码。 如果我无能为力,那么我不应该从事这项工作。 掌握新技能并紧跟最新技术是我高效完成工作的方式。 如果 我无法学习,我将无法做更长的工作。 […]

蘑菇12周

回望我在Gousto的Mobile Apps团队实习的12周 嗨,我是Nik! 我是伦敦帝国理工学院计算机科学专业的19岁学生,刚刚在Gousto完成了暑期实习,就职于iOS /移动团队“ Mushrooms”(现在的头衔很有意义)😄。 这是我的第一次实习,这是在公司的技术团队中研究真实产品的绝佳机会。 没错,我从事的一些工作现在已经向全世界展示,并希望也能使该业务受益。 这也是我第一次参加代码审查,既审查了我的代码又成为了审查代码的人。 我将尝试对我已经完成的工作以及在这里工作的感觉进行分类 在这篇文章中的12周。 我做了什么 在实习期间,我从事了几个小项目,以及团队其他成员的正常工作。 我真的很喜欢在实现特定部分的同时可以拥有一些独立性和所有权,同时还可以与团队的其他成员一起工作。 那么,我做了什么? 我的第一个提交和拉取请求实际上是一个小问题-该项目不会为我编译,因为上游项目配置错误,并且引用了之前已删除的某些依赖项-仅当项目被重新检出时,该项目才值得注意。 在最初的几天里,我正在设置开发环境并了解代码库。 我的直属经理Spencer首先让我与我的同事在各种小票上进行了一些配对编程:不仅为我提供了一个更好地了解代码的机会,而且还为以后的12周中与我共事的人们提供了机会。 除了各种门票,我还从事以下工作: 维护模式 我的首要任务之一是实施维护模式屏幕。 该屏幕很少使用,但允许Gousto在后端关闭时向用户显示消息,这当然是为了维护。 我以前在Xcode中使用Interface Builder的经验仅是在Mac应用程序上进行的,而此任务主要是在IB中进行的,因此这是一个使我更加习惯IB以及使用自动布局为iPad和iPhone设计的机会。 玩得很尽兴。 更改git-flow的Bitrise配置 蘑菇在许多方面都不同于Gousto的其他技术部门。 这些方式之一是我们使用“适当的” git-flow。 我们尝试在考虑所有分支和标签的情况下按书进行操作。 但这并不总是这样,之前,我们使用了类似git-flow之类的东西,但并不完全是这样。 该系统适用于其他具有即时部署功能的团队,但是由于我们在iOS中没有此功能,因此决定将其切换为git-flow。 Bitrise是我们在Mushrooms中使用的持续集成平台。 您提交一些东西,它由Bitrise编译和测试。 为了完成这项工作,我不得不更新各种触发器并进行测试,在此过程中,还创建了一个简单的沙箱应用程序来测试新配置。 屏幕恢复 屏幕恢复(或简称OSR)是一个大项目。 我在八月的大部分时间里都在进行此工作,创建了各种屏幕和逻辑以为我们的客户提供恢复体验。 成功已不是秘密,古斯托需要成长。 任何公司都一样。 阻碍增长的一件事是用户不了解我们的订阅服务而取消订阅。 OSR的目的是“恢复”客户,说服他们保持订阅状态比取消订阅服务更好。 有时,用户可能会因为对服务的理解不佳而退订,而且如果用户确实确实想退订,则Gousto可以弄清楚为什么用户会退订。 当我开始从事此项目时,OSR已经出现在网站上。 实际上,这是我从事的iOS前端。 这涉及创建许多屏幕,这些屏幕根据从服务器获得的响应中提供的逻辑进行显示。 这个想法是根据用户使用Gousto的时间为用户定制暂停过程。除了从设计中创建所有屏幕外,我还研究了服务器响应的客户端模型,并运行用户完成并管理旅程,行程前对服务器响应的验证以及分析的发送。 以下是一些屏幕截图: 其他的东西 经过深思熟虑后,决定OSR端点将服务Markdown,因此必须在客户端进行呈现。 由于我们不想使用Web视图,而是希望完全控制文本样式; 我写了一个小Markdown到NSAttributedString转换器,该转换器将使用Markdown字符串,将它们输入到名为cmark的C Markdown参考实现中,然后输出格式化的字符串。 在实习即将结束时,我担任了后端工程师的角色,将我们的REST库添加到其中一个PHP服务中。 该服务管理着我们常见问题的内容,将来将能够将这些内容提供给各种前端。 […]

Plank简介:iOS的不可变模型生成

Rahul Malik | Pinterest技术主管,iOS核心体验 去年,我们的iOS团队全面改革了整个应用程序的体系结构。 这是一项巨大的努力,导致开发人员可以更快地迭代应用程序,更易于扩展,而全球Pinners的应用程序则快3倍。 我们的新系统严重依赖于并发。 UI渲染,图像下载,GIF解码和网络响应处理只是利用多个线程来提高性能的一些领域。 这意味着这些组件使用的对象必须是线程安全的,以避免错误和潜在的崩溃。 由于模型对象几乎遍历我们应用程序的所有组件,因此确保模型层可以安全地跨线程使用非常重要。 为了解决这个问题,我们转到了一个不变的模型层。 不可变对象与可变对象的不同之处在于,一旦创建它们就无法对其进行修改,这从本质上使它们成为线程安全的。 这使开发人员可以编写更易于推理的代码,因为一旦建立不变量就无法更改。 今天,我们是开放源代码的Plank,这是我们为实现此目的而创建的iOS不可变模型生成器。 Plank是用Swift编写的命令行工具,可生成不可变的Objective-C模型。 在本文中,我们将重点介绍一些主要功能以及其创建的动机。 动机 设计和维护模型层可能很繁琐且容易出错。 缺少简单的null检查或尝试序列化包含无法序列化的属性的对象可能会导致未定义的行为和崩溃。 手写模型还可能遭受其实现中的不一致问题,并且在序列化时可能导致不同的行为和策略。 以下是一些由于手写模型问题导致的常见错误和崩溃的示例。 通过Plank生成模型 让我们用这些字段创建一个表示Pin的模型。 定义架构 Plank将模式文件作为输入,因此我们需要创建一个。 这是Pin类型的架构。 您会注意到我们指定了模型的名称及其属性列表。 请注意,该链接指定了一个附加的format属性,该属性指示Plank使用更具体的类型,例如NSURL或NSDate 。 产生模型 假设此架构另存为pin.json我们通过运行plank pin.json生成模型。 下面,我们将重点介绍Plank从您的模式生成的一些功能。 $木板pin.json Plank创建的Pin类接口 您会注意到的第一件事是所有属性都是readonly 。 这使该类不可变,但是它并没有真正的用处,因为我们没有办法用任何值填充Pin的实例。 为了解决这个问题,我们需要一个抽象,它将采用一组值并产生一个不可变的对象。 变异和建造者 通过生成器类执行Plank生成的模型中的变异。 这是构建器模式的直接实现,Plank会为您生成它。 builder类是一个单独的类型,其中包含readwrite属性和一个将创建新对象的build方法。 JSON解析 现在,我们有了一个不可变的模型和一个构建器类来创建新实例。 但是,大多数应用程序不是静态的,并且依赖于从API返回的JSON数据。 这是我们的Pin模型的示例JSON响应。 为了正确处理此响应,我们不仅需要断言响应类型是正确的,而且还需要添加其他逻辑以将链接表示为NSURL的实例。 小心处理null值也很重要,以避免将属性设置为NSNull值或将null传递给需要nonnull参数的API。 这些错误可能导致无法预测的行为和崩溃。 Plank将创建一个名为initWithModelDictionary的初始化方法,该方法处理解析符合您的模式的NSDictionary对象。 序列化 如果要为应用程序建立脱机支持或在应用程序启动期间保留数据,则需要将模型存储到磁盘。 iOS上最常规的解决方法是在每个模型上实现NSSecureCoding 。 […]

通过创造混乱来进行单元测试竞赛条件(快速)

多线程竞争条件不会始终如一地发生,这使得它们很难重现。 另一方面,最佳软件实践要求我们编写自动化测试以验证我们的更改。 我将分享我的技术来创建足够的混乱,以使那些罕见的崩溃变得足够可靠以进行测试。 什么是比赛条件? 如果您有多个线程试图同时变异和访问数据,可能会发生不好的事情。 一个线程可能会更改一个值,而另一个线程正在使用它! 避免这种情况的最好方法是以不可能的方式构造代码。 Swift的结构是一个很好的起点,因为它们是在写操作时复制的(额外的功劳:从功能上考虑!)。 但是我们不能总是做理想的事情。 我们在现实世界中发展,但我们的控制范围之外。 也许我们陷入了遗留代码库。 也许我们没有时间或金钱来重构所有东西。 有时,我们所能做的就是实施最佳实践。 最佳实践之一是: 修复错误时,编写一个可重现该错误的单元测试以防止退化。 通常这是直截了当的,但是这些竞争条件崩溃可能很棘手。 它们很少出现在崩溃报告器中,并且您从未在设备上看到过这种情况。 让我们崩溃 首先,设置一个包含一些不安全代码的简单方法: 不料? 更像预期的那样。 现在,您可以修复它,并且可以放心,将来的更改不会使它恢复正常。 肖恩每天在 Livefront对抗 混乱 。

使用迭代方法构建活动标签

您是否曾经与一位朋友进行过一项活动,但忘了记录下来? 如果您可以倒转时钟并记得进行录音,那岂不是很棒吗? 或者,也许您与一个没有录音的朋友一起跑步,并且想向Strava介绍。 你不是一个人! Strava社区的成员向我们提交了解决此问题的方法。 此功能请求有200多个注释和300个投票。 在成长团队中, 每项新功能均始于实验,而每项实验均以假设为前提,然后编写一行代码 。 任何新功能的主要假设之一就是人们将实际使用它。 有了来自社区的强烈信号,我们有能力以合理的信心投资于活动标签,以解决我们真正的运动员需求。 2017年底,成长团队开始研究活动标签。 此功能允许运动员将活动发送给忘记记录的朋友。 接收朋友可以接受邀请并将该活动保存到自己的个人资料中。 在此期间,我们的团队专注于增加运动员发送的邀请数量,活动标记是邀请朋友加入Strava的一种非常自然的方式。 活动标记并不是在今天运动员看到的情况下才首次启动的。 我们通过一系列仍在进行的迭代来获得当前版本。 我们构建此功能所采用的过程是我们如何在Strava的Growth Team进行迭代产品开发的完美示例。 通过将A / B测试和用户研究与较短的开发周期相结合,我们能够不断验证我们的假设,并确保我们构建的功能令人愉悦并能满足运动员和业务需求。 这篇博客文章将解释我们如何构建活动标记-我们最喜欢的功能之一,从而对成长团队的开发过程提供一些见解! 在花费较长的开发周期来构建理想功能之前,我们需要在现实世界中快速验证我们的假设。 在进行定于10月底开始的更大项目之前,我们只有两个星期的开发时间来构建和发布MVP(最小可行产品)。 第一步是设计一种可以在这些时间限制内建立的简单体验。 此功能有两个受众:有活动标签的发送者(我们称为邀请者)和有活动标签的接收者(我们称为邀请者)。 邀请者有两种基本体验:在您的活动中标记其他Strava运动员,或将活动标签发送给尚未在Strava上的人,以鼓励他们加入您在Strava上的活动。 作为一个团队,我们决定简化邀请者的体验,并将Strava网络内部和外部的标记体验结合起来,这样我们就只需要构建一个邀请者体验即可。 为了使所有这些工作正常进行,我们利用了“分支指标”链接技术,并依赖于生成映射到有关活动标签信息的URL链接。 当运动员想要标记他们的朋友时,我们创建了这些魔术URL之一,然后该运动员将该链接发送给他们的朋友,并带有诸如“嘿,我们一起进行了此活动,这是一个链接,因此您可以将活动复制到自己的轮廓”。 这些链接会将被邀请者在手机上与他们朋友的活动进行深层链接,他们将能够接受标签并将该活动复制到自己的个人资料中。 如果被邀请者还不是Strava运动员,他们将被带到应用商店,然后在下载时,看到欢迎屏幕,鼓励他们签名并接受活动标签。 我们建立的邀请者体验不是很理想。 我们利用iOS和Android共享托盘,允许运动员发送包含其活动标签的简单链接。 在被邀请方,当运动员点击链接并希望接受活动标签和新用户的简单欢迎屏幕时,我们会在活动页面上建立一个非常简单的横幅。 构建最复杂的软件是当有人接受活动标签时在服务器端创建新活动。 此外,我们保留了时间来围绕该功能构建安全性,因此不会被滥用。 尽管我们相当确定Strava运动员会使用此功能来标记其他Strava运动员,但我们确实希望了解该功能是否会推动新的注册。 我们的假设是,活动标记将比常规邀请吸引更多的邀请。 我们向90%的运动员发起了比赛,有10%的支持者参加了比赛,以确保我们了解此新功能的效果(例如是否会损害通过其他方法发出的邀请等其他指标)。 由于可以在我们的应用程序中非常流行的屏幕(活动屏幕)上访问该功能,因此我们很快吸引了很多运动员使用它。 最初的结果令人鼓舞:运动员发送的活动标签数量是常规邀请的五倍 ,并且我们的核心指标都没有受到此新功能的伤害。 由于我们的目标是构建一个性能比常规邀请更好的功能,因此我们认为这是一次胜利。 在着手构建更好的活动标记版本之前,我们寻找了优化当前邀请者体验的方法。 因为第一个版本相当基础,所以我们坚持进行两个简单的测试。 首先,我们将活动页面上的按钮从带有橙色边框的白色按钮更改为纯橙色按钮。 我们假设更突出的按钮将导致在“添加朋友”上的点击次数增加,结果将发送更多邀请。 在iOS上进行的测试结果显示,有7.6%的运动员在其活动页面上点击“添加朋友”,而同期这一比例为6.5%。 结果,发送的邀请数量增加了11%。 我们还测试了活动页面上按钮旁边的副本。 由于活动页面中包含很多内容,因此我们只能使用一小行作为活动标记功能的入口。 因此,为了快速向运动员传达此功能的价值主张,复制非常重要。 我们的原始副本为“添加尚未录制或尚未在Strava上的朋友”。 […]

iOS上的Shazam:UI测试+ Shazam Pro用户提示

在Shazam,我们从一开始就尝试使用UI测试。 您还记得用Javascript编写这些测试脚本吗? 但是直到最近,我们还无法真正将其集成到我们的流程中。 在Javascript时代(严重的是,有人还记得这种技术的名字吗?),每个人都必须编写代码来消除弹出窗口,即使引入了最小的更改,即使是最直接的测试也变得无法使用。 当引入XCUITest时,它肯定会变得更好,但我们还缺少一件事-应用程序间通信和多应用程序测试。 当我们不得不将CFNotificationCenter用于这些目的时,感觉并不正确,因此我们放弃了一段时间。 但是从XCode 9开始,我们现在只需引用捆绑标识符即可在不同应用之间运行,终止和通信。 让app1 = XCUIApplication(bundleIdentifier:“ com.Shazam.app1”) app1.launchEnvironment = [“键”:“值”] app1.launch()let app2 = XCUIApplication(bundleIdentifier:“ com.Shazam.app2”) app1.terminate() app2.launch() 因此,现在我们可以运行多个应用程序。 只需别忘了将所有应用程序添加到UITests目标的“目标依赖项”,XCode会自动将其安装在您的设备或模拟器上。 因此,在Shazam中,我们关心Shazaming,并使用这些简单的工具现在可以为我们的核心功能构建一些UI测试。 多年前,在一次研讨会上,我听肯特·贝克(Kent Beck)的演讲时,我记得他提到音频是编写自动化测试最困难的事情之一。 的确是正确的(我们拥有用于测试内部音频工具的更复杂的测试),但是使用这些新工具,我们可以创建一个简单的设置来测试我们所需要的。 我们要求PlaybackApp播放一些音频(我们使用外部URL来下载和播放音频文件)。 //启动播放应用并传递音频文件URLletplaybackApplication = XCUIApplication(bundleIdentifier:“ com.Shazam.TestPlaybackApp”)playbackApplication.launchEnvironment = [“ url”:playbackTest.urlString] playbackApplication.launch() 现在我们启动Shazam应用程序并启动Shazaming! //启动播放应用并传递音频文件URLlet shazam = XCUIApplication(bundleIdentifier:“ com.Shazam。***”) shazam.launch() 让shazamButton = … shazamButton.tap() 现在,我们等待是否显示正确的标题(实际上,我们做的有些不同,但这也可以工作)。 让artistLabel = shazam.staticTexts [“ 优素福·拉特夫(Yusef Lateef) […]