views:

60

answers:

2

I have an error similar to the one in this post. Now, I'm sure I've made some stupid error somewhere, probably related to releasing an object or an observer or what-not, but since I can't seem to find a way to debug the code I thought I could use the NSDebugEnabled, NSZombieEnabled and MallocStackLogging (as shown here).

Can it be done using OCUnit? If so, how? I just can't find an "executable" to set these parameters on...

Thanks! Aviad.

A: 

Well, NSZombieEnabled and friends are environment variables, which means they have to be run on an executable. The default setup for a unit testing bundle is for the tests to be run during the build process, and not during execution.

So the way to fix this is to make it so that your tests don't run during the build phase, but instead run them as part of an executable.

Here's how I do that:

  1. Inside your Unit Test bundle target, remove the "Run Script" build phase. It's that step that executes the tests after compiling them.
  2. From the Project menu, choose "New Custom Executable..." and name it something meaningful, like "otest"
  3. Make the executable path to be the otest binary, which should be located at /Developer/Tools/otest
  4. Set the following environment variables on the otest executable:
    • DYLD_FRAMEWORK_PATH => {UnitTest.bundle}/Contents/Frameworks
    • DYLD_LIBRARY_PATH => {UnitTest.bundle}/Contents/Frameworks
  5. Set the following program arguments on the otest executable:
    • -SenTest All (this will run all of the unit tests)
    • {UnitTest.bundle}

You can now select your unit test bundle as the active target, and the otest executable as the active executable, and then build and debug. This will let you set breakpoints, set other environment variables (like NSZombieEnabled), and so on.

If you only want to debug a certain suite or specific unit test, you can change the -SenTest All argument to -SenTest MyUnitTestSuite or -SenTest MyUnitTestSuite/myUnitTestMethod.

Dave DeLong
Haven't tried this yet, but it sounds very promising; what are the downsides for this method? In other words, if it gives so much, why isn't it the default template for Xcode?
Aviad Ben Dov
@Aviad my guess would be that this requires 2 additions to your project: the unit test target and the otest executable, and Xcode (AFAIK) doesn't provide a way to add both in a single step.
Dave DeLong
Just to be clear: when you write {UnitTest.bundle}, you want me to keep it that way exactly, or replace it with something?
Aviad Ben Dov
@Aviad sorry, that should be replaced with the name of your unit test bundle.
Dave DeLong
@Dave: So in your example, it would be "otest" (named in step 2?)
Aviad Ben Dov
@Dave: Forget my last comment. I suppose you meant the .octest bundle. I tried using it (including the .octest suffix; it wouldn't work otherwise) and it failed with: The test bundle at Tests.octest could not be loaded because its Objective-C runtime information does not match the runtime information required by the test rig. This is likely because the test rig is being run with Objective-C garbage collection disabled, but the test bundle requires Objective-C garbage collection.
Aviad Ben Dov
@Aviad Take out the `OBJC_DISABLE_GC` environment variable.
Dave DeLong
@Dave: Heh, I figured as much. Unfortunately, I just get the same error in reverse (i.e. it complains the test rig is being run with Obj C GC enabled but the test bundle does not support Obj C GC).. I'm confused!
Aviad Ben Dov
+1  A: 

Unfortunately, Dave's solution didn't work - I kept getting errors and mistakes. I eventually got GHUnit to work on my project, found the problem by debugging, but it had its own problems so I now use both it and OCUnit which is slightly better integrated in terms of showing the results in the results tab.

sigh. When will we get to see a good, complete unit testing framework for Obj-C?

Aviad Ben Dov