为啥在单元测试中使用 UITableViewCell 会导致 Trace/BPT 陷阱?

Posted

技术标签:

【中文标题】为啥在单元测试中使用 UITableViewCell 会导致 Trace/BPT 陷阱?【英文标题】:Why does using UITableViewCell from a unit test cause a Trace/BPT trap?为什么在单元测试中使用 UITableViewCell 会导致 Trace/BPT 陷阱? 【发布时间】:2010-09-06 13:32:36 【问题描述】:

我在以下创建的最小项目中有以下最小测试用例 GHUnit 自述文件:

#import <GHUnitios/GHUnitIOS.h>
#import <UIKit/UIKit.h>

@interface MyTest : GHTestCase  
@end

@implementation MyTest

- (BOOL)shouldRunOnMainThread 
  return YES;


- (void)testFoo 
  UITableViewCell *cell =
      [[UITableViewCell alloc] initWithStyle:UITableViewStylePlain
                             reuseIdentifier:@"foo"];

  NSLog(@"cell: %@", cell);
  NSLog(@"cell.textLabel: %@", cell.textLabel);


@end

当我在 Xcode 的模拟器中使用 Build and Run 时,它运行良好。然而,当我 在终端中运行以下命令:

GHUNIT_CLI=1 xcodebuild -target Tests -configuration Debug -sdk iphonesimulator4.0 build

我得到这个输出:

Running: /Users/<user>/Desktop/tmp/TestApp/build/Debug-iphonesimulator/Tests.app/Tests -RegisterForSystemEvents
Tests(39346) malloc: protecting edges
Tests(39346) malloc: recording malloc stacks to disk using standard recorder
Tests(39346) malloc: enabling scribbling to detect mods to free blocks
Tests(39346) malloc: process 39249 no longer exists, stack logs deleted from /tmp/stack-logs.39249.Tests.ac1JfL.index
Tests(39346) malloc: stack logs being written into /tmp/stack-logs.39346.Tests.t8LG4p.index
Test Suite 'Tests' started.
MyTest/testFoo 2010-09-06 23:24:25.006 Tests[39346:903] cell: <UITableViewCell: 0x5a6d190; frame = (0 0; 320 44); layer = <CALayer: 0x5a6d390>>
RunTests.sh: line 28: 39346 Trace/BPT trap          $RUN_CMD
Command /bin/sh failed with exit code 133
Command /bin/sh failed with exit code 133
** BUILD FAILED **

OCUnit 也会发生这种“Trace/BPT Trap”事情,我希望 GHUnit 会解决它,但它不在命令行上。任何人都知道什么 是关于?它似乎与在你不是的上下文中使用 UIKit 有关 应该的,但我不明白到底是什么限制。

【问题讨论】:

啊,我添加了更多代码并发现当我在命令行上运行时[NSApplication sharedApplication] 为空;我认为这与此有关。那么有没有一种简单的方法可以在不运行模拟器的情况下运行使用 UIKit 类的单元测试? 【参考方案1】:

有许多 UIKit 类根本无法在运行的 UIApplication 的上下文之外工作。例如,实例化任何尝试使用 UIFont(例如 UILabel)的东西都会在运行的 UIApplication 之外发生混乱(即分段错误或类似情况)。 UIActivityIndi​​catorView 同样会失败。

真的没有办法在模拟器或设备之外一致地测试依赖于 UIKit 的代码。幸运的是,您可以运行为模拟器编译的代码,而无需实际运行模拟器进程。我相信 iOS 测试的 GTM 设置可以做到这一点;我知道Cedar 是针对 iOS 规范执行此操作的。我对 GHUnit 还不够熟悉,无法说出它是否可以做到这一点。

如果你想从命令行运行这样的规范,你需要适当地设置一些环境变量。您需要将 DYLD_ROOT_PATH 设置为您要链接的 iOS SDK 的目录,将 IPHONE_SIMULATOR_ROOT 设置为同一 SDK 目录,并将 CFFIXED_USER_HOME 设置为非空目录(我使用随机临时目录)。设置好这些后,您可以通过直接调用模拟器 SDK 并在命令行中添加 -RegisterForSystemEvents 来执行针对模拟器 SDK 构建的二进制文件。

例如,您可以查看Cedar Rakefile(我最熟悉它,因为它是我编写的;其他同样有效的示例可能存在于 GTM 或 GHUnit 中)。查看 :uispecs 任务中文件的底部以了解它运行的命令行。

【讨论】:

【参考方案2】:

我在 GHUnit 中测试 UIViews 和 UIViewControllers 时遇到了类似的崩溃,当我从命令行运行时,因为没有用于绘制视图的界面。

只要您不调用导致它们将自己绘制到屏幕上的方法,您就应该能够测试其中一些类中的逻辑。只要未将其添加到主视图层次结构中,您就可以初始化视图。避开 UIView 中的 drawRect 和 UIViewController 中的 loadView 对我有帮助。

但是,如果您的视图中有需要测试的逻辑,移动逻辑可能会更容易?

【讨论】:

以上是关于为啥在单元测试中使用 UITableViewCell 会导致 Trace/BPT 陷阱?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在这个单元测试中没有调用 finalize 函数?

为啥我的单元测试在 Chrome 中通过而在 PhantomJS 中失败?

为啥 PyCharm 无法运行单元测试?

为啥使用集成测试而不是单元测试是一个坏主意?

为啥我在我的 Rspec 模型测试中得到单元化常量?

为啥?单元测试覆盖率中显示的类即使没有添加到测试目标中