tl; dr
在Pivotal,我们之所以写Cedar是因为我们在Ruby项目中使用并喜欢Rspec。Cedar并不是要取代OCUnit或与其竞争。就像Rspec率先在Ruby中进行BDD样式测试一样,它的目的是将BDD样式测试的可能性带到Objective C,但并未消除Test :: Unit。选择一个或另一个很大程度上取决于样式偏好。
在某些情况下,我们设计了Cedar以克服OCUnit为我们工作的方式中的一些缺点。具体来说,我们希望能够在测试中使用调试器,从命令行和CI构建中运行测试,并获得测试结果的有用文本输出。这些东西或多或少对您有用。
长答案
在两个测试框架(例如Cedar和OCUnit)之间进行选择取决于两件事:首选样式和易用性。我将从样式开始,因为这只是意见和偏好的问题;易用性往往是一系列的权衡。
样式方面的考虑超越了您使用的技术或语言。xUnit风格的单元测试比BDD风格的测试存在更长的时间,但是BDD风格的测试迅速流行,这在很大程度上归功于Rspec。
xUnit风格的测试的主要优点是它的简单性和广泛的应用性(在编写单元测试的开发人员中);几乎您可以考虑使用的任何语言都可以使用xUnit样式的框架。
与xUnit风格相比,BDD风格的框架往往有两个主要区别:测试(或规范)的结构方式以及编写断言的语法。对我而言,结构差异是主要差异。xUnit测试是一维的,对于给定测试类中的所有测试都有一个setUp方法。但是,我们测试的类不是一维的。我们经常需要在几种不同的,可能相互冲突的环境中测试动作。例如,考虑一个带有addItem:方法的简单ShoppingCart类(出于此答案的目的,我将使用Objective C语法)。当购物车为空时,与购物车中包含其他物品时相比,此方法的行为可能有所不同。如果用户输入了折扣代码,则可能会有所不同;如果指定的项目可以 不能按照所选的运输方式运输;当这些可能的条件彼此相交时,最终可能会出现几何数量不断增加的可能上下文。在xUnit风格的测试中,这通常会导致产生很多方法,例如testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies。BDD样式框架的结构使您可以分别组织这些条件,我发现这些条件使确保涵盖所有情况变得更加容易,并且更容易查找,更改或添加单个条件。例如,使用Cedar语法,以上方法如下所示:在xUnit风格的测试中,这通常会导致产生很多方法,例如testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies。BDD样式框架的结构使您可以分别组织这些条件,我发现这些条件使确保涵盖所有情况变得更加容易,并且更容易查找,更改或添加单个条件。例如,使用Cedar语法,以上方法如下所示:在xUnit风格的测试中,这通常会导致产生很多方法,例如testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies。BDD样式框架的结构使您可以分别组织这些条件,我发现这些条件使确保涵盖所有情况变得更加容易,并且更容易查找,更改或添加单个条件。例如,使用Cedar语法,上面的方法如下所示:
describe(@"ShoppingCart", ^{
describe(@"addItem:", ^{
describe(@"when the cart is empty", ^{
describe(@"with no discount code", ^{
describe(@"when the shipping method applies to the item", ^{
it(@"should add the item to the cart", ^{
...
});
it(@"should add the full price of the item to the overall price", ^{
...
});
});
describe(@"when the shipping method does not apply to the item", ^{
...
});
});
describe(@"with a discount code", ^{
...
});
});
describe(@"when the cart contains other items, ^{
...
});
});
});
在某些情况下,您会发现其中包含相同的断言集的上下文,您可以使用共享的示例上下文来进行干燥。
BDD样式框架和xUnit样式框架之间的第二个主要区别是断言(或“ matcher”)语法,只是使规范的样式更好。有些人真的喜欢,其他人则不喜欢。
这就带来了易用性的问题。在这种情况下,每个框架都有其优点和缺点:
OCUnit比Cedar长得多,并且直接集成到Xcode中。这意味着确定新的测试目标非常简单,并且在大多数情况下,启动测试并“正常运行”。另一方面,我们发现在某些情况下,例如在iOS设备上运行,几乎不可能使OCUnit测试正常工作。设置Cedar规范比OCUnit测试需要做更多的工作,因为您可以自己获取该库并对其进行链接(在Xcode中这绝不是一件简单的任务)。我们正在努力简化安装过程,欢迎任何建议。
OCUnit作为构建的一部分运行测试。这意味着您无需运行可执行文件即可运行测试。如果任何测试失败,则您的构建失败。这使运行测试的过程更简单了一步,并且测试输出直接进入您的构建输出窗口,这使得查看变得容易。我们出于某些原因,选择将Cedar规范内置到可执行文件中,然后将其单独运行:
- 我们希望能够使用调试器。您可以像运行其他可执行文件一样运行Cedar规范,因此可以以相同方式使用调试器。
- 我们想要简单的控制台登录测试。您可以在OCUnit测试中使用NSLog(),但是输出将进入构建窗口,您必须在其中展开构建步骤才能读取它。
- 我们希望在命令行和Xcode中都易于阅读测试报告。OCUnit结果很好地显示在Xcode的构建窗口中,但是从命令行进行构建(或作为CI进程的一部分)会导致测试输出与许多其他构建输出混合在一起。通过独立的构建和运行阶段,Cedar将输出分开,因此易于找到测试输出。默认的Cedar测试运行程序复制标准的打印样式“”。对于每个通过的规格,“ F”表示不合格的规格,等等。Cedar还具有使用自定义报告器对象的能力,因此您可以花费一点力气就可以以任意方式输出结果。
OCUnit是用于Objective C的官方单元测试框架,并得到Apple的支持。苹果公司基本上拥有无限的资源,因此,如果他们想做点什么,它就会完成。而且,毕竟,这是我们正在使用的Apple沙盒。但是,另一方面,Apple每天收到的支持请求和错误报告的数量也非常庞大。他们非常擅长处理所有问题,但它们可能无法立即或根本无法解决您报告的问题。Cedar比OCUnit新颖得多,而且功能较差,但是如果您有任何疑问,问题或建议,请发送邮件到Cedar邮件列表(cedar-discuss@googlegroups.com),我们将尽一切努力为您提供帮助。另外,请随时从Github(github.com/pivotal/cedar)派生代码,并添加您认为丢失的任何内容。
在iOS设备上运行OCUnit测试可能很困难。老实说,我已经有一段时间没有尝试过了,所以它可能变得更容易了,但是上次尝试时,我根本无法获得可用于任何UIKit功能的OCUnit测试。编写Cedar时,我们确保可以在模拟器和设备上测试与UIKit相关的代码。
最后,我们编写了Cedar用于单元测试,这意味着它与UISpec之类的项目无法真正相比。自从我尝试使用UISpec以来已经有一段时间了,但是我了解它主要集中于在iOS设备上以编程方式驱动UI。因为苹果当时(当时)即将宣布UIAutomation,所以我们特别决定不尝试让Cedar支持这些类型的规范。