Run logic tests in Xcode 4 without launching the simulator
Asked Answered
C

7

31

I want to run tests in Xcode 4 using OCUnit without launching the simulator. Please, don't try and convince me I am doing unit testing wrong or anything like that. I like to do TDD the traditional way: write the API for the class in the tests, then make the class pass the tests. I will write separate tests that are end-to-end that run in the simulator.

If there's no way to do this, then please can someone tell me how to have the test harness not instantiate the whole app? My app is event driven, and it sends a bunch of events through when it starts up that mess with my tests.

Crook answered 1/9, 2011 at 18:24 Comment(0)
G
28

Please can someone tell me how to have the test harness not instantiate the whole app? My app is event driven, and it sends a bunch of events through when it starts up that mess with my tests.

I use Xcode 4's built-in testing. App instantiation may seem like a pain, but as I write on Xcode Unit Testing: The Good, the Bad, the Ugly, it makes it possible to write tests without distinguishing between logic tests and application tests. Specifically, it lets me write unit tests for view controllers.

Here's what I do to avoid my full startup sequence:

Edit the scheme

  • Select the Test action
  • In "Test" select the Arguments tab
  • Disable "Use the Run action's options"
  • Add an environment variable, setting runningTests to YES

Edit your app delegate

  • Add the following to -application:didFinishLaunchingWithOptions: as soon as it makes sense to:

    #if DEBUG
        if (getenv("runningTests"))
            return YES;
    #endif
    
  • Do the same for -applicationDidBecomeActive: but simply return.

Update: I have changed my approach. See How to Easily Switch Your App Delegate for Testing.

Garibold answered 3/9, 2011 at 3:34 Comment(7)
I guess this only works for applications loading their GUI in the -application:didFinishLaunchingWithOptions:. What if you are building a app that uses storyboards?Knightly
Where should I actually put this code? My current target doesn't have AppDelegate at all. And if I add it to my UnitTest target, it doesn't change anything. (I am using XCode 4.6)Abjure
@VictorRonin What type of target do you have that it doesn't have an application delegate?Garibold
@JonReid:Good question. How do I check? This target doesn't have Summary tab and when I viewed project.pbxproj, it saw that productType = "com.apple.product-type.bundle"; for this target. So, I assume that it builds a bundle. What type of target do I need?Abjure
@JonReid: BTW. If you have a working sample, I would really appreciate it and I can figure it out from there.Abjure
@VictorRonin Since you're working on a bundle, this SO question doesn't apply to you. To write unit tests against your bundle, you'll need a unit-testable target that loads your bundle.Garibold
@JonReid: Sorry for confusion. My main target is an iOS application. I have second target, which contains all of my tests. It's a bundle.Abjure
B
6

In the last xcode version (5.0.2) you can do this in very easy way. Choose your Test target, "General" tab. Set "None" in field "Target". Then tap on "Build phases" tab and remove your Main target from "Target dependencies".

Bedard answered 5/1, 2014 at 10:0 Comment(3)
Are you sure is it enough? I've tried the steps reported, but it still launches the simulator. Many thanksLakesha
To fix the compile errors, ensure all frameworks are linked that your test cases require (usually the same ones your .app links to) and ensure your build phases compiles all the source files your test cases require also.Nashom
Great Answer! For those where this solution isn't working: note that (with at least Xcode 7+), there are two testing targets: AppNameTests and AppNameUITests. In "Edit Schemes"->"Test"->"Info" both of these test targets are enabled for testing. Deselect "UITests" here. If "UITests" is selected, this has a Build Phase that will launch the simulator.Botzow
C
3

In your situation, I am assuming that you have a separate Logic Tests and Application Tests target (if not - you need to). In your schemes configuration you define which targets are built for the 'Test' scheme. If your application tests are not running, the simulator will not launch.

I suspect that you might be trying to run 'logic tests' in an 'Application tests' target (such as the one created by default by Xcode). See more about this difference here (and how to set ut up).

Cenogenesis answered 1/9, 2011 at 18:53 Comment(0)
L
2

It was pointed out in an earlier answer that logic tests are the right thing to do for this scenario. I had very tough time in getting the logic tests working with XCode Version 4.3.2 (4E2002). Looking at Apple's sample unit test project helped me to understand how to do this with a clear separation. In that example, logic tests test files from the library target, not the application target. The model was encapsulated into a library which was then linked with the main target and logic tests target. The application target contained only views and controllers.

Based on this model, this is what I did to get my logic tests work correctly. Create a new target (Cocoa Touch Static Library) and move all files to be logic tested (typically all your models) to this new target. Under "Build Phases" settings add this new library in "Link Binary With Libraries" of your application target and logic tests target.

I can imagine that these instructions are little confusing. If you dissect the sample project that is mentioned above you will get a better idea.

Libelant answered 21/4, 2012 at 7:41 Comment(0)
N
2

Note, untested on Xcode 5.

I used @jon-reid’s answer, but found that Xcode adds environment-variables to the xcuserstated part of XcodeProjects, and these are user specific and not typically committed to the repository. Thus I swizzle my AppDelegate to override its loading:

@implementation MyAppDelegate (Testing)

+ (void)initialize {
    SEL new = @selector(application:didFinishLaunchingWithOptions:);
    SEL orig = @selector(swizzled_application:didFinishLaunchingWithOptions:);
    Class c = [self class];
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);

    if (class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
        class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    } else {
        method_exchangeImplementations(origMethod, newMethod);
    }
}

- (BOOL)swizzled_application:(id)app didFinishLaunchingWithOptions:(id)opts {
    return YES;
}

@end

Note, that the following is simpler and still works, though I'm not sure it is reliable:

@implementation MyAppDelegate (Testing)

- (BOOL)application:(id)app didFinishLaunchingWithOptions:(id)opts {
    return YES;
}

@end

This works because categories of methods in dynamically loaded components (like the testing bundle) take precedence. Swizzling feels safer though.

Nashom answered 3/10, 2012 at 13:4 Comment(2)
Where should I actually put this code? My current target doesn't have AppDelegate at all. And if I add it to my UnitTest target, it doesn't change anything. (I am using XCode 4.6)Abjure
You put this in the test target, and you rename MyAppDelegate to whatever your AppDelegate's classname is in your main target. Your test target doesn't have an app delegate, but that's the point of this question, the app delegate from the main target is still executed.Nashom
E
1

Using xCode 7 and xctool

xctool is capable of executing unit tests without the simulator.

To get this working,

1 . Update the target settings run without a host app.

Select your project --> then test target --> Set the host application to none.

enter image description here

2. Install xctool , if you don't have it.

brew install xctool

3. Run the tests using terminal with xctool.

 xctool -workspace yourWorkspace.xcworkspace -scheme yourScheme run-tests -sdk iphonesimulator
Employee answered 25/5, 2016 at 9:23 Comment(0)
C
0

i've used GHUnit to create osx/ios compatible test suites. there are a few issues, but i found it was more reliable/compatible/straightforward than OCUnit.

GHUnit provides basic template projects for OS X and iOS, which makes initial setup simple.

Note: I generally just use my own kit for most of my testing.

Captor answered 1/9, 2011 at 19:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.