如何在 Xcode 4.5“命令行工具”项目中设置工作逻辑单元测试目标?
Posted
技术标签:
【中文标题】如何在 Xcode 4.5“命令行工具”项目中设置工作逻辑单元测试目标?【英文标题】:How to set up working logic unit tests target in Xcode 4.5 "Command Line Tool" project? 【发布时间】:2012-11-02 15:18:39 【问题描述】:无法为特定场景设置单元测试。这是我正在尝试的:
在 Xcode 4.5 中,我创建了一个简单的 OSX “命令行工具” 应用程序项目(基础)。 请注意,Xcode不提供自动将单元测试添加到“命令行工具”项目的选项——所以请不要建议勾选勾选框;它不存在:-/
在我的项目中,我创建了一个我想测试的简单示例类;例如“形状”。
我按照 Apple 的 Xcode 单元测试指南 中针对 Setting Up Unit-Testing in a Project 的说明进行操作:
我在我的项目中添加了一个单元测试目标,并且
我编辑了“测试”方案以在新目标中运行测试。
在测试项目的实现 (.m) 文件中,我添加了 Shape.h
的导入和 setUp()
方法中的代码以实例化形状并将其分配给实例变量。
那时,我决定看看事情是否会构建以及默认测试是否会继续运行。但是,当我从菜单中选择 Product...Test 时,构建失败并出现以下错误:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_Shape", referenced from:
objc-class-ref in ExampleTests.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
解释这个错误不是问题。我认为单元测试目标没有链接到包含 Shape 实现的二进制文件。但是,我(还)不了解 Xcode 单元测试和目标配置。所以:
为了让测试目标链接到命令行工具的结果输出,我需要做什么?我可以从单元测试目标链接到命令行可执行文件吗? Apple 的文档看起来特定于常规 OSX 应用程序 (*.app
) 和 ios 应用程序,而这两者都不是。
我有想要在命令行工具设置中开发的业务逻辑类(首先),所以我想了解我需要做什么才能让单元测试目标在“命令行工具”类型的项目。 谢谢!
(p.s. 请注意,我不对在命令行运行我的单元测试从感兴趣 - Stack Overflow 已经提出了关于如何做到这一点的“类似”问题 -而是在“命令行工具”类型项目上运行单元测试,并且仍然在 Xcode 中。)
【问题讨论】:
【参考方案1】:我已经确定了一个我认为合适的解决方法,除了添加目标之外,它似乎没有明显的缺点。
简而言之:解决方案涉及添加静态库目标,以利用 Xcode 围绕此类目标创建和运行单元测试代码的能力。然后命令行工具目标委托给静态库,其中一个替代的main()
类函数由真正的main()
入口点定义和调用。命令行工具不包含重要代码,因此单元测试目标可以访问所有值得测试的内容。
步骤如下:
从一个空的 Xcode,从菜单中选择 File...New Project。
在出现的对话框中,选择OS X...应用程序...命令行工具。出于本示例的目的,我假设它被命名为 SampleCmd。
基本命令行工具项目创建完成后:
从菜单中选择文件...新建...目标。
在出现的对话框中,选择OS X...Framework & Library...Cocoa Library。出于本示例的目的,我假设它被命名为 SampleCmdLogic。
选择类型Static,这样命令行工具将保持为独立的可执行文件。
确保选中包括单元测试框。
静态库项目创建后:
将main()
函数从main.m
复制到SampleCmdLogic.m
,替换@implementation
块。 (此文件将仅包含主入口点。可以为 Objective-C 类等添加其他文件。)重命名函数为libMain()
。
在SampleCmdLogic.h
中,为新的libMain()
添加声明,替换@interface
块:int libMain(int argc, const char * argv[]);
在命令行工具的main.m
顶部添加#import "SampleCmdLogic.h"
。
在命令行工具的main.m
中,将真正的main()
函数的全部内容改为:return libMain(argc, argv);
代码现已准备就绪,但需要执行链接步骤:
在 SampleCmd
的项目设置中,在 Build Phases 下,展开 Target Dependencies 并添加 (+) SampleCmdLogic 作为依赖项。
在 SampleCmd
的项目设置中,在 Build Phases 下,展开 Link Binary With Libraries 并添加 (+) libSampleCmdLogic.a
现在一切都准备好了。当您更改为 SampleCmd 目标并从菜单中选择 Product..Run 时,构建应该会成功并按预期生成输出。当您更改为 SampleCmdLogic 目标并从菜单中选择 Product...Test 时,构建应该会成功并且单元测试将运行。报告的单个问题将是 Xcode 在SampleCmdLogicTests.m
中插入的初始默认失败单元测试断言。这是意料之中的。
从此时开始,继续将所有逻辑和相应的测试添加到 SampleCmdLogic 目标。 SampleCmd 目标将保持微不足道并仅提供命令行工具入口点。
【讨论】:
搞笑:我刚刚发现自己需要解决这个问题!感谢您提出一个简单而优雅的解决方案。【参考方案2】:将测试目标添加到应用项目中通常需要许多额外的步骤——特别是我在https://***.com/a/12624873/246895 中描述的 Bundle Loader 和测试主机的设置。
但是当我使用命令行工具进行测试并尝试运行测试时,它所做的只是运行该工具。对于应用程序,它会经历启动应用程序、将测试包注入正在运行的应用程序、然后执行测试的阶段。但这些阶段不适用于命令行工具。
因此,您需要的不是注入的测试包,而是运行测试的第二个命令行工具。然后设置您的类,以便它们针对测试工具以及您的实际工具。 gh-unit 和 google-toolbox-for-mac 都遵循这个模型,所以我会尝试它们。
【讨论】:
谢谢,这至少让我放弃了让命令行工具直接参与单元测试的尝试。我改为使用静态库目标将 Xcode 单元测试支持带回到我的项目中。参考我的回答;有助于支持著名的格言“计算机科学中的所有问题都可以通过另一个间接层次来解决”(尽管这更多是工具问题,而不是科学问题。) 我会接受我自己对此的回答,但您会因提供洞察力让我思考解决方法而获得奖励。【参考方案3】:似乎最直接的问题只是Shape
没有包含在新的测试目标中。尝试将Shape.m
添加到测试目标:
-
在 Project Navigator 中单击 Shape.m
打开实用程序视图(视图 -> 实用程序 -> 显示实用程序)
在实用程序视图的 Target Membership 部分中,确保您的应用 和 测试目标均已选中。
我不知道这是否是您的设置问题的结束,但它似乎可能是您最直接的问题的候选者。
【讨论】:
谢谢。我知道在这样做时,我只是将要重新编译的源文件包含在单元测试二进制可执行文件的一部分中,而不是链接到命令行工具目标并使用已编译的代码。在最简单的情况下,我认为这会产生一个工作单元测试项目,但不是一个理想的解决方案,因为在单元测试项目中包含和重新编译源文件可能会引入细微的差异;例如它不一定是相同的结果代码正在测试。在实际项目中,编译器选项、定义的符号等可能会有所不同,n'est-ce pas?以上是关于如何在 Xcode 4.5“命令行工具”项目中设置工作逻辑单元测试目标?的主要内容,如果未能解决你的问题,请参考以下文章
Xcode 4.5 命令行工具 - xcode-select 问题
如何在 BQ 命令行中设置 Bigquery 需要分区过滤器
在`pygments`的命令行工具`pygmentize`中设置html字体大小