I’ve realized that I’ve found a way to solve an incredibly persistent problem experienced by many.

Apple’s unit test bundles can not be run from within Xcode with breakpoints set. If a test fails, this is fine, but if a test just plain blows up, how do you debug it? I’ve talked to a few local people and the answer seems to be, "You don’t." Or, "Add lots of NSLog() messages." But here’s a secret way to run the unit test bundle under gdb:

Find gdb

If you are developing for the Mac, this isn’t a problem - it’s in the path. If you are developing for iOS, we need to run in the simulator and use the gdb from the iOS SDK. You can use xcodebuild to find the correct one:

$ xcodebuild -sdk iphonesimulator4.2 -find gdb
/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/gdb

Tell gdb to wait for the test runner

otest is the program which runs a unit test bundle. You can use gdb’s -waitfor option to tell gdb to wait for the otest program to start.

$ $(xcodebuild -sdk iphonesimulator4.2 -find gdb) -waitfor otest
GNU gdb 6.3.50-20050815 (Apple version gdb-1510) (Wed Sep 22 02:45:02 UTC 2010)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin".
Waiting for process 'otest' to launch.

Run your unit test bundle

Run your unit test bundle from Xcode. You will see gdb respond:

Attaching to process 45973.
Reading symbols for shared libraries . done
Reading symbols for shared libraries ................... done
0x8fe2dcfa in __dyld_mach_msg_trap ()

(gdb)

You are now attached to the process; however, your unit tests have not been loaded. If you simply need a stack trace from a SIGABRT, type "continue" and press enter, and you will stop at the fault with the ability to use "backtrace" to get a stack trace.

Otherwise, you can set a breakpoint on a particular line using break Foo.m:151 or at a particular method using break -[FooClass fooMethod]. Since your code isn’t loaded yet, you will have to indicate that you want the break point set up when code is dynamically loaded. You can then use "continue" to continue until your breakpoint is hit.