在 Xcode 中进行自动化测试 (1/2)

这是 iOS 教程团队成员 Charlie Fulton 的一篇博客, 一个全职的 iOS 开发者, 喜欢和家人一起打猎, 钓鱼。

所有的开发者都需要测试他们的软件, 并且一些聪明人会创建测试套件来达成这个目标。测试套件就是一系列测试用例的集合, 也被称作单元测试, 用于测试小”单元”的代码, 通知是一个特定的方法或者类。

单元测试能让你应用测试驱动开发的实践, 并且保证你的代码产生更少的 bug。并且为你找到的每一个 bug 创建单元测试, 可以帮助你确保这个 bug 不会再发生!通过预先的测试和调试, 你能极大的减少你的应用在离开开发环境后出现异常的机会。

但是也知道, 我们经常着急把功能完成 – 或者我们比较懒 – 在你每次修改代码后都能自动的运行单元测试那会更好。 这对于你作为一个团队的成员时更为重要。

所以, 如果你能够有这样一个系统, 自动化的构建, 测试然后将你的应用提交给 beta 测试者, 你会对它感兴趣吗? 如果是的话, 那么继续读下去吧!

这个奖励的章节和下一个, 将会带你为你的应用搭建一个自动化的构建和测试系统。你将会得到一个示例项目, 给他添加一些单元测试, 和一个远程 GitHub(github.com) 代码仓库, 然后设置一个持续集成(CI)服务器 Jenkins。 Jenkins 将会每隔一段时间检测你的 GitHub 仓库的更改情况, 然后自动的构建,测试,并且把你的应用提交到 TestFlight 上面!

如果你不了解自动化测试, 你可能会对上面的一些(或者说是所有)术语感到疑惑, 包括 GitHub,Jenkins 和 TestFlight。我们将会在这个教程中详细讨论每一个, 但是现在, 有一个快速的对这些工具的微型讲解:

  • GitHub – 免费的公共 Git 仓库, 协作管理, 问题追踪, Wiki, 下载, 代码评审, 图, 还有更多!
  • Jenkins – 一个持续集成服务器和调度器, 可以监测你的 Git 仓库的变化, 自动开始构建和测试, 并且把结果通知给你。
  • TestFlight – 自动将你的 iOS 应用发布给测试者的服务器, 并且能进行崩溃日志分析。

这个简短的讲解应该能让你在短时间内摆脱困境 – 给我一些时间来介绍 geektastic 示例项目, 这将是你在这个教育历险中的交通工具 – 但做好准备在后面学习更多内容!

GuildBrowser 简介

在这个教程中, 你将要为一个叫做 GuildBrowser 的应用创建自动构建和测试系统。GuildBrowser 是一个简单的应用, 可以让你浏览一个流行的游戏,魔兽世界中一个公会所有会员的列表。你可以输入一个公会和王国的名字, 然后在 UICollectionViewController 中看到所有成员的列表, 就像是你在下面看到的:

这个应用是基于暴雪公司免费提供的魔兽世界 API 来创建的。你将会用到 AFNetworking 框架来创建一个 REST 风格的,对魔兽世界 API 的调用, 获得包含公会和角色信息的 JSON 数据。

如果你没有时间来玩这个游戏, 那么最好的就是做一个应用来帮助游戏!

注意:
关于魔兽世界 API 的详细信息, 签出暴雪在 GitHub 上面的项目 (https://github.com/Blizzard/api-wow-docs),魔兽世界官方 API 文档(http://blizzard.github.com/api-wow-docs/),魔兽世界 API 论坛(http://us.battle.net/wow/en/forum/2626217/)。

开始吧

如果你还没有对你的代码用到过版本控制, 那么你绝对不会正确的使用它!可以证明, 现在最流行的版本控制系统是 Git。Linus Torvalds(没错,他是 Linux 的作者) 设计并开发了 Git, 相比它的前任, SVN, 是一个更好的系统, 用来管理 Linux 内核的代码。Git 不仅是一个非常棒的版本控制系统, 并且它直接集成进了 XCode, 这样能够在 iOS 开发中方便的使用到它。

Git 是一个“分布式”的版本控制系统, 意味着每个 Git 工作目录, 都是它自己的仓库, 并不依赖于中央服务器。

但是, 当你希望将你的本地仓库共享给其他开发者时, 你可以在 “remote” 设置中定义一个连接。远程仓库是那些通常会放到远程中央服务器中的你的项目中的某些版本。他们甚至能通过 Dropbox 目录来共享(虽然这不是推荐的方法)。

最近几年出现了一系列的服务, 可以用来更简单的设置远程 Git 仓库,和其他分布式的版本控制系统。GitHub 有充分的理由,已经成为了这些服务中最好的之一。它提供免费的公共仓库, 协作管理, 问题追踪, Wiki, 下载,代码审查,图和更多。 很抱歉,听起来有点像广告, 但是 GitHub 确实非常棒!

注意: 关于 Git 更多的详细描述和你怎样使用它, 可以看一下即将发布到 raywenderlich.com 中的教程: 如何在 iOS 6 下使用 Xcode 进行 Git 源代码控制。

你在这篇教程中第一件要做的事情就是在 GitHub 建立一个远程仓库(简写为 repo)。 它将成为你的”原始”远程仓库。它能够让你在本地照常工作,然后当你准备好和其他团队成员进行同步或者将构建好的应用发送给测试者的时候, 你将会把所有的本地提交 Push 到远程仓库中去。

注意:
这篇教程假设你已经有了一个带有 SSH Key 的 GitHub 账号。如果没有的话,请看一下这个指南(https://help.github.com/articles/generating-ssh-keys),这里告诉你通过生成 SSH 秘钥对来设置账号。你还可以用带有你的账户和密码的 https URL 来访问 GitHub,但是这篇指南假设你用的是 SSH Key。

首先, 确保你安装了命令行工具,选择 Xcode/Preferences/Downloads 选项卡,保证你的屏幕看起来是这样的(最重要的是保证 Installed 显示在 Command Line Tools 后面):

这篇教程来自一个起始项目,你可以到这里 下载。将这个项目拷贝到你硬盘上的某一个位置, 然后在 XCode 中打开GuildBrowser

GuildBrowser 将会创建成 Single View Application 模板, 使用 ARC, 单元测试, Storyboard, 仅支持 iPad 和一个本地 Git 仓库。编译并且运行, 你应该看到应用会从默认的公会中显示出一些角色。

打开一个浏览器, 进入 github.com 并且登陆进你的账号, 或者如果你没有账号的话, 创建一个。 然后, 点击右上角的Create a New Repo 按钮:

在 Repository name 和 Description 字段中, 输入你喜欢的内容 – 这个名字不一定和你在 Xcode 中的项目名一样。然后点击 Create repository 按钮。

注意:
不要选中 Initialize this repository with a README 选项。 你要创建一个空的仓库并且将你的 Xcode 项目放进来。

通过点击 copy to clipboard 按钮或者 ⌘-C 复制一下这个仓库的 SSH URL(记得首先点击 SSH 按钮, 切换到 SSH URL)。

现在打开 Organizer (Cmd-shift-2) 然后选择 Repositories 标签。

你应该会在底部看到 GuildBrowser 项目。 点击 Remotes 并且点击 Add Remote 按钮。

在 Remote Name 中输入 origin

Enter origin for the Remote Name, 并且把你刚刚赋值的 GitHub Url 粘贴到 Location 中:

点击 Create, 然后 Organizer 应该看起来是这样:

不要着急为什么没有输入密码的地方。 你将会使用 Git 的 SSH 访问, 所以不需要密码。

你现在已经设置到远程仓库了! 你可以对你所有的 Xcode 项目应用这个步骤。这非常棒, 但是你仍然还没有将你本地的 Git 仓库 Push 到远程仓库中。 现在我们来用 Xcode 做吧。

返回到你的项目中, 并且确保你提交了所有本地的修改。 如果你们有提交, Xcode 将不会让你进行 Push 操作。 这里是步骤:

  1. 进入 FileSource ControlCommit (⌥⌘-C), 并且在下面的屏幕中, 输入一个提交消息, 像是 “initial commit”, 并点击 Commit
  2. 现在你需要将你本地的 master 分支提交到远程的 origin/master 分支中。 进入 FileSource ControlPush 和 点击 Push
  3. 因为这是你第一次提交到这个仓库中, 你应该会在 Remote 名称 origin/master 的旁边看见一个 (Create)。 点击Push 按钮, Xcode 将会处理其余的事情

一旦 push 完成了, 回到你的浏览器, 刷新页面, 你提交的修改将会显示在 GitHub 仓库中:

你应该还会在 OrganizerRepositoriesGuild Browser projectRemotes: 的列表中看到你的远程仓库和它的 master 分支。

祝贺你, 你知道了如何将你本地的 Git 仓库连接到 GitHub 的远程仓库中, 还有如何将修改 push 到它上面!

现在你设置好 GitHub 仓库了, 你可以邀请其他人来克隆你的仓库, 接受 pull 请求,或者看一下你的代码。当其他人 push 他们的代码到 GitHub 仓库的 origin/master 中, 你可以 FileSource ControlPull (⌥⌘-X),在弹出框中, Remote 应该设置成 origin/master, 选择 Choose 然后那些修改将会添加到你的项目中。

要看一下都有哪些修改, 进入 OrganizerRepositoriesGuildBrowser, 展开箭头, 并点击 View Changes 按钮。

持续集成

持续集成
(CI) 是一种通过服务器来连续的监测你的版本控制系统的修改, 从而自动构建并测试你(甚至是发布)的代码的系统。

当你有大量的开发者每天会提交很多次修改到项目中的时候, CI 会变得非常重要。如果任何冲突或错误发生了, 每个人都会立即得到通知。 最终,一些人打断了构建。

了解 Jenkins

Jenkins (http://jenkins-ci.org) 是一个开源的 CI 服务器, 你将会在这个教程中用它来自动构建,测试和发布 push 到 GitHub 远程仓库中的代码。

这节剩余的内容将包含如何让 Jenkins 工作运行起来。

安装 Jenkins

有几种不同的方式可以将 Jenkins 安装到 MAC 上:

  1. 你可以用 Jenkins 网站上面提供的安装器, 将会把 Jenkins 设置为守护进程。 这个的优点是, 它可以使用 Mac OS X 的启动系统, 但它的缺点对你和用户来说就是要对 Xcode 进行一些额外的设置才能让它正常的工作。
  2. 你可以从命令行中运行 WAR(Java 归档文件类型) 文件, 将会用内置的 servlet 容器来运行 Jenkins。
  3. 你可以将 WAR 文件部署到你已经设置好的应用程序容器中,例如 Tomcat, JBoss 等等。

在这个教程中, 你将会从命令行中运行 Jenkins, 因为它是最快,最简单的方式。从这里下载 Jenkins 的 WAR 文件http://mirrors.jenkins-ci.org/war/latest/jenkins.war。 打开命令行窗口, 然后运行如下命令:

  1. nohup java <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">-</span>jar ~<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Downloads<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>jenkins.war <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">--</span>httpPort<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">=</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(36,0,217)">8081</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">--</span>ajp13Port<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">=</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(36,0,217)">8010</span> > <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>tmp<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>jenkins.<a href="http://www.opengroup.org/onlinepubs/009695399/functions/log.html" style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,104,55); text-decoration:initial"><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(166,19,144)">log</span></a> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(36,0,217)">2</span>><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">&</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(36,0,217)">1</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">&</span>  

注意:
上面的代码假设你的 Jenkins WAR 文件在你的 Downloads 目录中。如果你将 WAR 文件下载到其他的位置, 相应的调整一下命令行中的路径。

注意:
你可能会发现,为 Jenkins 创建一个别名很有用。 对于命令行来说,或者可以显示隐藏文件的文本编辑器,打开/Users/(username)/.bash_profile 并且输入如下内容:

  1. alias jenkins<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">=</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(191,29,26)">"nohup java -jar ~/jenkins.war --httpPort=8081 --ajp13Port=8010 > /tmp/jenkins.log 2>&1 &"</span>  

保存 .bash_profile, 打开一个新的终端, 现在可以输入 jenkins 来启动它。

这个命令在 8081 端口上面打开 Jenkins。 nohup 是一个 UNIX 命令, 可以让你在注销后或者 Shell 退出后还能继续运行一个进程 – 它是 “no hangup” 的缩写。

上面的命令行还在 /tmp/jenkins.log 中建立了一个日志文件。我比较喜欢设置一下端口, 这样可以避免和我已经运行的其他服务相冲突。 可以在浏览器中打开如下的 URL 来访问 Jenkins:

配置 Jenkins 插件

在你开始设置作业的时候你还需要给 Jenkins 添加一些东西。打开 Jenkins 的主面板 (通过 http://localhost:8081) 并且点击 Manage JenkinsManage PluginsAvailable 标签。 因为有很多可用的插件, 所以需要在右上角的过滤搜索框中输入 Git 来过滤插件列表。

这里是你需要的插件:

选中 Git Plugin 旁边的复选框, 并且点击 Install without restart 按钮。 完成安装后, 你应该会看到如下的界面:

现在在过滤搜索框中输入 Chuck Norris, 并找到 Chuck Norris 引用插件。虽然对于这个教程,这个插件是可选的, 但是我非常推荐你选中它! 如果你没有安装它, 他会知道的!小心 Chuck 的愤怒。 :]

注意:
我的朋友叫我 Charlie, 但是我真正的名字是 Charles。 我父亲的名字是 Norris。所以我可以合法的给我的一个儿子命名为 Chuck Norris Fulton… 但是很不幸, 不让我这么做. :]

设置 Jenkins 电子邮件通知

如果能有一个 SMTP 服务器,能让 Jenkins 把构建过程中的错误通知给你, 那就最好了。但是如果你没有运行 SMTP 服务器, 你可以在 Jenkins 中使用 Gmail 账号 (或者其他任何你喜欢的 SMTP 服务器)。

进入 Manage JenkinsConfigure System, 并且在 Email Notification 部分, 输入如下设置(使用 Advanced Settings 按钮来访问其中一些设置):

  • SMTP Server : smtp.gmail.com
  • Sender Email address :
  • Use SMTP Authentication
  • User Name:
  • Password:
  • Use SSL
  • SMTP Port:465

现在, 通过点击 Test configuration by sending test-email 来测试一下 email 通知, 输入一个 email 地址, 并且点击 Test configuration 按钮。 一旦你确定测试邮件成功之后, 记得点击 Save 按钮来保存你设置的更改。

现在你能够从 Jenkins 那里接收到构建失败的邮件通知了。

创建一个 Jenkins 作业

你现在已经准备好创建一个 Jenkins 作业了! 打开 Jenkins 主面板, 点击 New Job, 输入 GuildBrowser 作为作业名称, 选择 Build a free-style software project 选项,最后点击 OK 按钮。 你应该能够看到作业的配置屏幕。

在 Source Code Management 部分, 点击 Git 选项,并且在 Repository URL 文本域中输入你的 GitHub 远程仓库(origin/master) 的URL。你的屏幕看起来应该是这样:

进入到 Post-build Actions section, 从 Add post-build action 中选择 Email Notification, 输入接受者的 email 地址, 最后选中 Send email for every unstable build. , 你应该看到如下内容:

如果你安装了 Chuck Norris 插件, 你还可以添加一个构建后脚本动作到 Activate Chuck Norris。 这听起来有点吓人! 警告, 不要打断构建!!

注意:
Chuck Norris 写的程序没有版本号, 因为他只写这个程序一次。如果一个用户报告了 bug 或者提了一个功能需求, 他们不会看到太阳升起。 :]

不要忘记点击 Save 按钮。

让我们点击左边栏的 Build Now 按钮来确保每个部分都于 GitHub 正确的设置好了。当作业开始的时候, 你将会在 Build History 看到一个更新:

如果一切运转正常的话, 你的 #1 号构建将会是蓝色的, 表示构建成功:

如果想看一下构建过程中实际发生了什么, 把鼠标移到这次的构建上面, 然后看一下 Console Output:

你的控制台输出看起来应该像是这样, 显示出这个作业成功的从 GitHub 仓库中获取到了代码:

到目前, 你通过你的作业和 email 通知连接到了 GitHub 仓库。 让我们进入最有趣的部分: 测试,构建,并且将你的项目上传到 TestFlight 上面。

注意: 一个 Jenkins 的专业技巧!如果要快速的进入设置界面, 把鼠标悬停在屏幕顶部面包屑尾部作业名称上, 然后在弹出的菜单中选择 Configure。 这在你正在看控制台输出的时候非常有用。

使用 Jenkins 自动执行 xcodebuild

让我们更新你的 Jenkins 作业来构建项目。进入 Jenkins DashboardGuildBrowser jobConfigure。 在 Build 部分中, 点击 Add build step 下来菜单, 选择 Execute shell

在命令行窗口中输入如下代码:

  1. export DEVELOPER_DIR<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">=/</span>Applications<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Xcode.app<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Contents<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Developer<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>  
  2.    
  3. xcrun xcodebuild clean build  

你的命令行窗口看起来应该是这样的:

现在点击 Save.
让我们点击 Build Now 再次测试一下这个作业。当这个作业开始执行的时候, 请注意!你将会看到下面这个界面, 询问你是否允许 Jenkins 运行 codesign 来对你的代码进行签名。 选择 Always Allow

如果你忽略了个提示框,你将会在你的作业的控制台输出中看到这个消息:

Command /usr/bin/codesign failed with exit code 1
** BUILD FAILED **
The following build commands failed:
CodeSign build/Release-iphoneos/GuildBroswer.app
(1 failure)
Build step ‘Execute shell’ marked build as failure

 

如果是这种情况的话, 那么试试重新运行一下这个作业, 这次不要再忽略这个提示框了。:]

在写这篇文章的时候, 我认真的用 “查找我的 iPhone” 来定位我自己的 iPhone, 小淘气把它放到了他的玩具车里了!!

假设你们有忽略这个提示框,那么你会在你的输出中看到类似这样的消息:

/Users/charlie/.jenkins/workspace/GuildBrowser/build/Release-iphoneos/GuildBrowser.app
Unable to validate your application. – (null)

这意味着这个构建成功了! – 你可以安全的忽略掉 “unable to validate” 这个消息 – 这是一个已知的 bug, 我们在今年的 WWDC 中确认过了。

注意:
有一个用于构建 iOS 应用的 Jenkins 插件,叫做 Xcodeplugin。 它能很好的工作, 但是我觉得最好还是能在你构建你的应用的时候了解实际发生了什么。你可以用一些你自己写的构建脚本来达到和这个插件同样的目的。

而且, 完全依赖于插件会带来一些风险。如果苹果修改了这些命令的工作方式, 那么这个插件就会失效, 并且它不再更新了, 如果你知道它内部是如何工作的话, 那么你仍然能够适应新的环境。

单元测试

单元测试确保你在开发的时候,你的代码输出不会改变。一旦建立了一个类将做什么事情的期望之后, 你通过写单元测试来验证这些期望是否达到。单元测试可以让你增量的进行修改,一步一步的看到结果,能够更容易的添加新功能和进行大的修改。

其他一些在你的项目中包含单元测试的好处:

  • 可靠性 – 通过一系列好的单元测试,你将更可能的减少 bug 数量。这对于那些从刚开始开发就应用测试的代码更加有效。对没一点修改和代码进行验证。
  • 更加可信的代码 – 通过单元测试可以减少那些伤脑筋的重构大量的代码,要知道你的代码在经过单元测试的时候仍然要产生同样的结果。
  • 稳定性 – 当找到 bug 的时候, 应该添加相应的单元测试, 来保证同样的 bug 不会再发生。

Xcode 通过 SenTestingKit 框架, 对单元测试进行了内建的支持。当写单元测试的时候, 有两个术语需要知道:

  • 测试用例 – 单元测试中最小的单位是测试用例。它用来验证你的某一单元的代码是否能输出期望的结果。例如, 一个测试用例可能聚焦于某一个方法的测试, 或者你的类中一组方法的测试。在 Xcode 中, 所有的测试用例必须以 test 开头。你将会在下面看到例子。
  • 测试套件 – 一组测试用例。这个通常是相对于一个特定类的一组测试用例。在 Xcode 中, 有一个创建测试套件的模板Objective-C test case class。 它会产生一个 SenTestCase 类的子类。

有两个通用的方法来创建单元测试:你可以自底向上(非别的测试每个方法和类)的测试, 或者自顶向下(测试应用整体的功能)的测试。 这两个方式都很重要 – 所以一般情况下, 最好将这两种方式结合起来使用。

在这个教程中, 你将会自底向上的测试, 聚焦于一部分模型类。在 iOS 6 by Tutorials 中, 有另一章, 你将会试着自顶向下的测试, 通过 UI Automation 从用户界面到模型代码进行测试。

应用程序 vs 逻辑测试 target

接下来, 让我们看一下 Xcode 中两种不同类型的单元测试。

应用程序单元测试

应用程序单元测试基本概念是需要用到 UIkit 的任何代码, 或者需要运行到宿主应用的环境之下。这也是你在创建带有单元测试的项目时默认的类型。

这个 target 所有的代码都会在你应用的 bundle 里面。你可以在 build settings 中对比每个 target 的 Test Host 和Bundle Loader 设置来找到它们的不同之处:

逻辑单元测试

目前, 这是唯一一种你可以在命令行运行的单元测试。 这个代码是独立于应用之外测试的。

这些是测试你自己代码用的单元测试 – 基本上所有这些都不需要运行在模拟器上面。 对于测试逻辑,网络访问代码,或者你的数据模型类非常有用。

Running tests from command line notes – no touching!

很不幸, 在 Xcode 4.5 中,我们仍然没有 “官方” 的允许, 来通过 xcodebuild 命令来运行应用程序单元测试。这非常不爽, 因为很明显你能够从 Xcode 中运行单元测试。 由于这个原因, 你将会在下一部分创建一个特定的逻辑测试 target。

一些有创造性的开发者已经对 Xcode.app contents 目录中的测试脚本打了补丁。在这个教程中, 你将会从 Jenkins 中运行你的逻辑测试, 从 Xcode 运行应用程序测试。 如果你对这个补丁很好奇, 可以看一下这些文件:

  1. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Applications<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Xcode.app<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Contents<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Developer<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Tools<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>RunUnitTests  
  2. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Applications<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Xcode.app<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Contents<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Developer<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Tools<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>RunPlatformUnitTests.include  

这些是你通过 ⌘-U 运行单元测试时默认会运行的脚本:

  1. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Applications<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Xcode.app<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Contents<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Developer<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Platforms<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>iPhoneSimulator.platform<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Developer<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>Tools<span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,34,0)">/</span>RunPlatformUnitTests  

这里是你将得到的错误消息, 原因是我们不能从命令行中将应用程序测试运行到模拟器中(这个在 Xcode 中可以正常运行):

RunTestsForApplication() {
Warning ${LINENO} “Skipping tests; the iPhoneSimulator platform does not currently support application-hosted tests (TEST_HOST set).”
}

一些聪明的开发者已经能让应用程序测试正常的工作了, 但是这个脚本在 Xcode 4.4 到 4.5 的更新中被修改了, 所以这样做会有风险。

添加逻辑测试 target

你将会创建一个新的测试 target, 可以让你从构建脚本中调用。 在 Xcode 中, 进入 FileNewTarget…iOSOtherCocoa Touch Unit Testing Bundle 然后点击 Next.

输入一个以 Tests 结尾的名称, 例如 GuildBrowserLogicTests, 确保 Use Automatic Reference Counting 被选中, 然后点击 Finish

你将会得到一个同名的新 scheme。 为了测试一切运转正常, 切换到新的 schem(使用 XCode 工具条 Run 和 Stop 按钮旁边的 scheme 选择器), 然后进入 ProductTest (⌘-U).
你应该看到构建成功了, 但是测试失败了。

就像是错误消息指示的那样, 这是因为单元测试还没有被实现。 让我们修复它!

首先, 删除默认的测试类, 通过选中 GuildBrowserLogicTest.h 和 GuildBrowserLogicTest.m, 点击 Delete, 然后选择 Move to Trash.

你不需要记住切换 scheme,让我们修改一下主scheme, GuildBrowser, 来为运行 ProductTest 时,添加新的 target。

切换到 GuildBrowser scheme, 并且通过 ProductEdit Scheme 来编辑 scheme, (或者你可以直接按住 Alt 并点击 Run 按钮)。 在 scheme 编辑器中, 选中 Test 配置, 点击 + 按钮来添加一个新的 target, 在窗口中选择GuildBrowserLogicTests, 然后点击 Add

你的屏幕应该看起来是这样的:

你现在已经准备好添加单元测试类到这个测试 target 了。 你将在随后可以从 jenkins 作业中调用这个 target。

你将会在下一部分继续完成它!Stay Hungry!

posted @ 2016-01-21 15:06  想吃天鹅肉的井底之蛙  阅读(1041)  评论(0编辑  收藏  举报