17

I'm trying to unit test some iphone code that instantiates fonts. I've narrowed it down to the following crashing unit test:

#import "test.h"
#import <UIKit/UIKit.h>


@implementation test

- (void)testFonts {
  [UIFont systemFontOfSize:12];
}

@end

This crashes with the error:

Test Case '-[test testFonts]' started.
/Developer/Tools/RunPlatformUnitTests.include: line 415: 79768 Trace/BPT trap          "${THIN_TEST_RIG}" "${OTHER_TEST_FLAGS}" "${TEST_BUNDLE_PATH}"
/Developer/Tools/RunPlatformUnitTests.include:451: error: Test rig '/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/Developer/usr/bin/otest' exited abnormally with code 133 (it may have crashed).

It seems like there's some setup I'm not doing in my unit test target to make this work. How do you unit test things that instantiate fonts?

Scotty Allen
  • 12,897
  • 9
  • 38
  • 51
  • You should provide the console output for better debugging. However there is no additional step needed for accessing a font, the way you do it is just fine. The problem is either: not including / linking against the correct version of UIKit or somewhere else in your code. – Till Nov 14 '09 at 13:47
  • Getting this crash as well. Any attempt to call systemFontOfSize: completely destroys it. /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.0.sdk/Developer/usr/bin/otest' exited abnormally with code 133 (it may have crashed). – featherless May 31 '10 at 06:23
  • Hey, is there anything I can do to make the answer below more complete / helpful? If not... i can has acceptanceburger? – beOn Mar 22 '12 at 20:44
  • 2
    UIFont is part of UIKit, which can't be tested in unit tests. You could try mocking the calls to UIFont. – Robert Wagstaff Aug 08 '12 at 03:55
  • This question may be irrelevant. Take a look at http://stackoverflow.com/q/41404613/62 for more. – Liron Yahdav Dec 31 '16 at 01:22

6 Answers6

19

One of my friends was recently running into this, and I dug out the last project I successfully set this up on. The trick is to run any of your tests that involve UIFont in application tests, rather than unit tests. Here are a few things to make sure of when setting up your application test target:

  1. BUILD PHASES: Bring your production code into your test target by including your main target as one of your test target's dependencies, rather than including the .m files in your test target. This is just for organization.
  2. BUILD SETTINGS: Make sure your test target's "Bundle Loader" is set to $(BUILT_PRODUCTS_DIR)/Your Product Name.app/Your Product Name Again. Take note, it's .../Product.app/Product - that is, there is no .app in the final segment of the path. If you're curious to see the file you're referring to, find your build product, right click, select View Package Contents, then find the executable.
  3. BUILD SETTINGS: Your test target's "Test Host" should be set to $(BUNDLE_LOADER). Easy peasy.
  4. BUILD SETTINGS: The test target's Other Linker Flags should include -framework SenTestingKit.
  5. BUILD SETTINGS: "Test After Build" should be No.
  6. SCHEME: 'Build' should include both your test target, and your main target, with "test" the only box selected in both targets.
  7. SCHEME: 'Test' should include your test target only. The files it includes will automatically be added to the list.

... hopefull this'll be enough to get you all going. It's not all cleanly documented in one place, and figuring out how to get both kinds of tests running took more time and pain than I'd like to admit.

It's strange that Apple seems to have pulled down their own doco on the matter. I wonder if there's another change coming...

Please hit me up if you have any additions to the above. It's not a complete guide to setting up Application testing, but the above are the undocumented stumbling blocks I ran into. FMI, I highly recommend this CocoaWithLove article.

beOn
  • 1,799
  • 1
  • 12
  • 13
  • For step two in your list: is it possible to generalize "Your Product Name" somehow, via some kind of variable? The reason I'm asking is that we have a separate iphone and ipod app that share the same code, but don't share device-specific resources (images and xibs and the like). Whenever I run the unit tests i have to pick one scheme and use that, as the unit test bundle only can reference one app that way. I'd like to make it generic so that i could have either one selected when I test. ${PRODUCT_NAME} doesn't work, since it refers to the unit test bundle itself, not the app bundle. – Kevlar Mar 14 '12 at 21:34
  • Hmm... good question. Let me make sure I understand what you're after... you want one target that can be used to test both apps? I don't know if that's going to work out. How will you indicate which app you want the testing target to test? What's confusing me is the sentence "I'd like to make it generic so that I could have either one selected when I test." Either one what? (continuing in next comment) – beOn Mar 22 '12 at 20:28
  • NOW... if you want to have two separate testing schemes, each of which would test a different app, that's not so hard. I don't know about using a variable for the product name, but with two targets you could just use different constant values in their respective Build Settings. That's the way I'd go with this one. If I've totally misunderstood you (always possible ;)), explain what you're after a little more and I'll try to help out. – beOn Mar 22 '12 at 20:29
  • By generic I mean not hardcoding the app target name so that I can have one single unit test bundle be able to test my iPhone target and my iPad target. I don't want to have to select the iPhone app scheme if the ipad target is selected before testing :). I realize i can just clone my existing unit test bundle and alter the settings, but having to maintain both is something i'd rather avoid, especially as we also have (for complex reasons) another copy of our iphone and ipad target that incorporates different code, which means i'd have to maintain 4 unit test bundles instead of one or two. – Kevlar Mar 23 '12 at 21:54
  • Ideally the maintenance shouldn't go beyond setting each of them up... once you've got them made, you'll still only need the one set of unit test files. Now, within those files you may want to fork off and do some special tests when you're running certain targets, but you could do that by #defining SOMETHING 1 in the targets pch file, then using #ifdef. Yes, you'll have to select and run them individually, but you should be doing that programmatically, anyway :). But you won't have to forever be going into the targets and dickering around... only the once. – beOn Mar 29 '12 at 15:41
  • The annoying thing with multiple almost-identical test targets is that when I or another developer want to add a new test file, we have to make sure that the new test class is properly associated with each test target. two targets aren't too bad, but four is much more of a hassle. It's not a huge deal, but it's something I'd like to optimize a bit if possible :) – Kevlar Apr 03 '12 at 02:19
  • You could, perhaps, have an intermediate test target, and include it in both of the real test targets as a dependency. Then you'll only have to add it to the one... – beOn Apr 22 '12 at 14:41
  • 1
    Just a quick note, I have a library that I work on where I ran into the same problem. I didn't have an application for the steps that needed it. But it turns out you can just create an empty application target and use it just for this. Thanks @beOn. – jasongregori Nov 30 '12 at 21:47
9

It doesn't state it very well, but Apple's Testing Kit splits unit tests into two separate categories:

  • Logic Tests

    These tests check the correct functionality of your code in a clean-room environment.

  • Application tests

    These tests check the functionality of your code in a running application.

There appears to be a lot of UI related code that can't be run in the "Logic Test" case. More information about Logic Tests vs Application Tests here.

http://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/UnitTesting/01-Unit-Test_Overview/overview.html

Frizlab
  • 846
  • 9
  • 30
dmaclach
  • 3,403
  • 1
  • 21
  • 23
2

I'm seeing this exact problem with 3.2 (and 3.1.3). I've seen it in two separate machines, so I don't think that my SDK is broken.

I created a new iPhone view based project, and added a unit test, and one test case.

This is set up as a logic test.

Console output is as follows:

Test Case '-[TestTests testTests]' started.
/Developer/Tools/RunPlatformUnitTests.include: line 415: 21141 Trace/BPT trap               "${THIN_TEST_RIG}" "${OTHER_TEST_FLAGS}" "${TEST_BUNDLE_PATH}"
/Developer/Tools/RunPlatformUnitTests.include:451: error: Test rig  '/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.3.sdk/Deve loper/usr/bin/otest' exited abnormally with code 133 (it may have crashed).
Command /bin/sh failed with exit code 1

If I set up the unit test for debugging, then I can see the crash, with the following stacktrace:

#0  0x00342d51 in __HALT 
#1  0x002947c7 in _CFRuntimeCreateInstance
#2  0x00b8441e in GSFontCreateWithName
#3  0x028c8f31 in +[UIFont systemFontOfSize:]

I can see Kendall's point (UIKit stuff may only work on the device), but that doesn't seem to be documented very well anywhere.

1

Have you tried it on the device? I seem to remember you can only include UIKit stuff in tests when running on the device, not against the simulator...

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • FYI UIKit stuff works just fine in unittests when using Google Toolbox for Mac. http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting – dmaclach Jul 12 '10 at 20:55
  • 1
    I have the same issue. It works on device and on simulator, but crashes if I launch tests in command line. – Quentin Apr 26 '11 at 15:47
0

Sorry for digging it up, but I have some issue with XCT test on Xcode 5 and it's related with this thread

Quote:

"Many UIKit classes won't work without the UIApplication Trying to add code to pass the tests in this project revealed that other tests would have to be conditionally excluded from LogicTests as well:

The default loadView method will fail — so the testLoadView method is #ifdef'd out.

Any attempt to alloc/init a UILabel will fail. I don't know why but because of this, all tests on the labels must be #ifdef'd.

Generally, don't expect anything from the UIKit framework to work in your Logic tests. Other frameworks will almost always work (in this application, the CoreLocation and Foundation frameworks work without issue).

Within UIKit, some elements will work — for example, UIWebView didn't have any issues in my testing. Exactly which user-interface objects will fail in the LogicTests bundle (without an actual running application) is never really clear until you try but these failures are why you still need to run the Application Tests to verify against — the Application Tests are a more authoritative result for anything in UIKit."

URL: http://www.cocoawithlove.com/2009/12/sample-iphone-application-with-complete.html

palusik
  • 111
  • 11
0

Select your Unit Test target in the "Targets" section from the projects/targets list and under the "General" section choose your Host app as the main app, which has the fonts.

That solved the issue for me.

nemesis
  • 1,349
  • 1
  • 15
  • 30