ER: Don't start NSApplication when injecting tests into a dependent target

Originator:ddribin
Number:rdar://7333645 Date Originated:24-Oct-2009 11:43 AM
Status:Open Resolved:
Product:Developer Tools Product Version:Xcode 3.2
Classification:Enhancement Reproducible:N/A
 
24-Oct-2009 11:43 AM Dave Dribin:
When running a unit test bundle against a GUI application, the test bundle gets injected into the application, and the the tests are run after a delayed performSelector or something.  This means that a lot of the NSApplication startup machinery is still run:

  * The main window NIB is loaded
  * All -awakeFromNib methods are called
  * -applicationWillFinishLaunching: is called

While this can be useful if you are doing higher-level integration testing, this is not good for true low-level unit testing where you are testing individual classes in isolation:

  <http://xunitpatterns.com/unit%20test.html>
  <http://misko.hevery.com/2009/07/14/software-testing-categorization/>
  <http://www.artima.com/weblogs/viewpost.jsp?thread=126923>

First off, it slows down the tests by loading the NIB and running code that does not affect the tests.  Worse is -awakeFromNib or -applicationWillFinishLaunching: may adversely interfere with tests by kicking off timers and background threads or registering for notifications.  They could even block app startup by showing a modal window.

I once had to add code that skipped part of app initialization if it was being run under OCUnit (by checking for the existence of the SenTestCase class).  This is a code smell due to putting test logic in production code:

  <http://xunitpatterns.com/Test%20Logic%20in%20Production.html>

I've gotten around the problem altogether by replacing main() with the following code.  However, it would be nice if this was built into the default NSApplicationMain machinery somehow.

#import <Cocoa/Cocoa.h>

int DDTestApplicationMain(int argc, const char ** argv)
{
    BOOL testMode = (getenv("TEST_HOST") != NULL);
    if (!testMode) {
        return NSApplicationMain(argc, argv);
    } else {
        [[NSRunLoop currentRunLoop] run];
        return 0;
    }
}

int main(int argc, char *argv[])
{
    return DDTestApplicationMain(argc, (const char **)argv);
}

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!