如何运行和调试 iPhone 应用程序的单元测试
注意:现在单元测试的设置要容易得多。本教程与 Xcode 5 及更高版本并不真正相关。
我花了相当长的时间,但我终于设法让它适用于我的项目。 为了创建“逻辑”测试,我遵循 Apple 关于创建逻辑测试的指南。 一旦您了解逻辑测试是在构建期间运行的,这就可以正常工作。
为了能够调试这些测试,需要创建一个将调用这些测试的自定义可执行文件。 Sean Miceli 在 Grokking Cocoa 博客上发表的文章提供了执行此操作的所有信息。然而,遵循它并没有立即取得成功,需要进行一些调整。
我将回顾 Sean 的教程中介绍的主要步骤,提供一些“傻瓜式”大纲,这花了我一些时间来弄清楚:
- 设置一个包含单元测试但不运行它们的目标
- 设置 otest 可执行文件来运行测试
- 设置otest 环境变量,以便 otest 可以找到您的单元测试
以下是使用 XCode 3.2.5 执行
的 XCode 4 注意事项
在 XCode 4 中,可以直接调试单元测试。只需编写您的测试,将其作为测试之一添加到您的目标并在其中设置断点即可。就这样。更多的将会到来。
步骤 1 - 设置目标
- 复制位于项目目标下的单元测试目标。这还将创建单元测试产品的副本(.octest 文件)。下图中“LogicTest”是原始目标。
- 将单元测试目标和单元测试产品(.octest 文件)重命名为相同的名称。在下图中,“LogicTestsDebug”是重复的目标。
- 删除新目标的 RunScript 阶段
两者的名称可以是任何名称,但我会避免使用空格。
步骤 2 - 设置 otest
这里最重要的一点是获取正确的 otest,即用于您当前的 iOS 版本而不是默认的 Mac 版本。 Sean 的教程对此进行了很好的描述。以下是一些帮助我正确设置的详细信息:
- Go Project->New Custom Executable。这将弹出一个窗口,提示您输入可执行文件名称和可执行文件路径。
- 输入您想要的任何名称。
- 复制粘贴 iOS otest 可执行文件的路径。就我而言,这是 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/Developer/usr/bin/otest
- 按 Enter 键。这将带您进入可执行文件的配置页面。
- 此时唯一需要更改的是选择“路径类型:相对于当前 SDK”。不要输入路径,这是在步骤 3 中完成的。
步骤 3 - 设置 otest 参数和环境变量
otest 参数的设置很简单...但是事实证明这是我最大的问题。我最初将逻辑测试目标命名为“LogicTests Debug”。使用这个名称和“LogicTests Debug.octest”(带引号)作为 otest 的参数,我一直让 otest 以退出代码 1 终止,并且永远不会停止到我的代码中...
解决方案:您的代码中没有空格目标名称!
otest 的参数为:
- -SenTest Self(或 All 或测试名称 - 在终端中键入 man otest 以获取列表)
- {LogicTestsDebug}.octest - 其中 {LogicTestsDebug} 需要替换为您的逻辑测试包名称。
以下是用于复制/粘贴的环境变量列表:
- DYLD_ROOT_PATH: $SDKROOT
- DYLD_FRAMEWORK_PATH: "${BUILD_PRODUCTS_DIR}: ${SDK_ROOT}:${DYLD_FRAMEWORK_PATH}"
- IPHONE_SIMULATOR_ROOT: $SDKROOT
- CFFIXED_USER_HOME: "${HOME}/Library/Application Support /iPhone Simulator/User"
- DYLD_LIBRARY_PATH: ${BUILD_PRODUCTS_DIR}:${DYLD_LIBRARY_PATH}
- DYLD_NEW_LOCAL_SHARED_REGIONS: YES
- DYLD_NO_FIX_PREBINDING: YES
请注意,我也尝试了 DYLD_FORCE_FLAT_NAMESPACE,但这只会导致 otest 崩溃。
步骤 4 - 运行您的 otest 可执行文件
要运行您的 otest 可执行文件并开始调试您的测试,您需要:
- 设置将活动目标设置为单元测试目标(在我的例子中为 LogicTestsDebug)
- 将活动可执行文件设置为 otest 可执行文件
您可以构建并运行可执行文件并使用断点调试测试。
附带说明一下,如果您在运行 otest 可执行文件时遇到问题,则可能与以下问题有关:
- 路径错误。最初我遇到了很多问题,因为我指的是 mac otest。我在启动时不断崩溃,终止代码为 6。
- 参数错误。直到我从捆绑包 (.octest) 名称中删除空格之前,我一直遇到 otest 崩溃,退出代码为 1。
- 环境变量中的路径错误。 Sean 教程有很多后续问题,让您了解其他人的尝试。我现在的这套似乎有效,所以我建议你从这个开始。
您可能会在控制台中收到一些消息,这可能会让您认为环境变量有问题。您可能会注意到有关 CFPreferences 的消息。此消息不会阻止测试正常运行,因此如果您在运行 otest 时遇到问题,请不要关注它。
最后,一旦一切正常,您将能够在测试中的断点处停止。
最后一件事...
我在许多博客上读到集成 XCode SenTestKit 的主要限制是在构建应用程序时无法运行测试。事实证明,这实际上很容易管理。您只需将逻辑测试包添加为应用程序项目的依赖项即可。这将确保在构建应用程序之前构建逻辑测试包,即运行所有测试。
为此,您可以将逻辑测试包拖放到应用程序目标上。
NOTE: Unit testing is a lot easier to setup nowadays. This tutorial is not really relevant for Xcode version 5 and above.
It took me quite some time but I finally managed to make it work for my project.
To create the "logic" tests I followed Apple guidelines on creating logic tests.
This works fine once you understand that the logic tests are run during build.
To be able to debug those tests it is required to create a custom executable that will call those tests. The article by Sean Miceli on the Grokking Cocoa blog provides all the information to do this. Following it however did not yield immediate success and needed some tweaking.
I will go over the main steps presented in Sean's tutorial providing some "for dummies" outline which took me some time to figure out:
- Setup a target that contains the unit tests but DOES NOT run them
- Setup the otest executable to run the tests
- Setup the otest environment variables so that otest can find your unit tests
The following was performed with XCode 3.2.5
Note for XCode 4
In XCode 4 it is possible to debug your unit tests DIRECTLY. Just write your test, add it to your target as one of the tests and set a breakpoint in it. That's all. More will come.
Step 1 - Setting up the target
- Duplicate your unit tests target located under your project Targets. This will also create a duplicate of your unit tests product (.octest file). In the figure below "LogicTest" is the original target.
- Rename both the unit tests target and the unit tests product (.octest file) to the same name. In the figure below "LogicTestsDebug" is the duplicate target.
- Delete the RunScript phase of the new target
The name of both can be anything but I would avoid spaces.
Step 2 - Setting up otest
The most important point here is to get the correct otest, i.e. the one for your current iOS and not the default Mac version. This is well described in Sean's tutorial. Here are a few more details which helped me setting things right:
- Go Project->New Custom Executable. This will pop open a window prompting you to enter an Executable Name and an Executable Path.
- Type anything you wish for the name.
- Copy paste the path to your iOS otest executable. In my case this was /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/Developer/usr/bin/otest
- Press enter. This will bring you to the configuration page of your executable.
- The only thing to change at this point is to select "Path Type: Relative to current SDK". Do not type in the path, this was done at step 3.
Step 3 - Setting up the otest arguments and environment variables
The otest arguments are straightforward to setup... But this proved to be my biggest problem. I initially had named my logic test target "LogicTests Debug". With this name and "LogicTests Debug.octest" (with quotes) as argument to otest I kept having otest terminating with exit code 1 and NEVER stopping into my code...
The solution: no space in your target name!
The arguments to otest are:
- -SenTest Self (or All or a test name - type man otest in terminal to get the list)
- {LogicTestsDebug}.octest - Where {LogicTestsDebug} needs to be replaced by your logic test bundle name.
Here is the list of environment variables for copy/pasting:
- DYLD_ROOT_PATH: $SDKROOT
- DYLD_FRAMEWORK_PATH: "${BUILD_PRODUCTS_DIR}: ${SDK_ROOT}:${DYLD_FRAMEWORK_PATH}"
- IPHONE_SIMULATOR_ROOT: $SDKROOT
- CFFIXED_USER_HOME: "${HOME}/Library/Application Support/iPhone Simulator/User"
- DYLD_LIBRARY_PATH: ${BUILD_PRODUCTS_DIR}:${DYLD_LIBRARY_PATH}
- DYLD_NEW_LOCAL_SHARED_REGIONS: YES
- DYLD_NO_FIX_PREBINDING: YES
Note that I also tried the DYLD_FORCE_FLAT_NAMESPACE but this simply made otest crash.
Step 4 - Running your otest executable
To run your otest executable and start debugging your tests you need to:
- Set your active target to your unit test target (LogicTestsDebug in my case)
- Set your active executable to your otest executable
You can build and run your executable and debug your tests with breakpoints.
As a side note if you are having problems running your otest executable it can be related to:
- Faulty path. I had lots of problem initially because I was pointing to the mac otest. I kept crashing on launch with termination code 6.
- Faulty arguments. Until I removed the space from bundle (.octest) name I kept having otest crash with exit code 1.
- Wrong path in environment variables. Sean tutorial has lots of follow-up questions giving some insight on what other people tried. The set I have now seems to work so I suggest you start with this.
You may get some message in the console which might lead you to think something is wrong with your environment variables. You may notice a message regarding CFPreferences. This message is not preventing the tests from running properly so don't focus on it f you have problems running otest.
Last once everything is working you will be able to stop at breakpoints in your tests.
One last thing...
I've read on many blogs that the main limitation of the integrated XCode SenTestKit is that tests cannot be run while building the application. Well as it turns out this is in fact quite easy to manage. You simply need to add your Logic tests bundle as a dependency to your application project. This will make sure your logic tests bundle is built, i.e. all tests are run, before your application is built.
To do this you can drag and drop your logic test bundle onto your application target.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这篇文章的目的是作为一个“操作方法”,而不是一个真正的问题。因此,这个答案只是为了让我将“操作方法”标记为“已回答”。这可能会被社区标记为不规则。我正在寻求有关在哪里发布未来“操作方法”文章的建议。
关于这个主题的最后一点。
对于那些仍然想知道编写单元测试是否值得的人,我肯定会说是的!
我目前正在使用 CoreData 编写一个应用程序,并从 Web 服务检索数据(xml 解析)。可以测试和调试完整的模型,而无需:
为了调试 xml 解析,我可以简单地使用我完全控制的“硬编码”文件。
当然,关键是在代码中实现功能时编写测试。就整个应用程序的调试而言,它确实可以节省时间。
瞧,我就这样吧。
This post is intended as a "How-to" more than a real question. Therefore this answer is just meant to allow me to mark the "How-to" as "answered". This will probably be flagged by the community as irregular. I'm up for suggestions on where to post future "How-to" articles.
One final note though on this topic.
For those who still wonder whether writing unit tests is worth it I would definitely say Yes!
I am currently writing an application with CoreData and retrieval of data from a web service (xml parsing). The complete model can be tested and debugged without having to:
To debug the xml parsing I can simply use "hard-coded" files which I completely control.
The crux is of course to write the tests as you implement features in the code. It really is a time saver down the line in terms of debugging of the complete application.
Voilà, I'll leave it at that.
我能够通过以下简单步骤在调试器中运行测试用例:
这是在 Xcode 6.0.1 上进行的,看起来比上面描述的长过程方便得多。
I was able to run the test case in debugger in the following simple steps:
This is on Xcode 6.0.1 and seems much more convenient than the long procedure described above.