isKindOfClass and NSStringFromClass disagree about UIApplicationDelegate
Asked Answered
W

2

14

I was playing with a simple OCUnit test for an iPhone app, and just wanted to assert that the app delegate was an instance of the class that I expected it to be. I didn't expect this test to be very useful, but it turned out to reveal a misunderstanding that I have regarding Objective C.

I first get a reference to the delegate. Then I log the class name of what comes back. In my case, the output correctly says "app delegate's class name is CalculatorAppDelegate".

However, the assertion on the next line fails, and I don't understand why.

- (void)testAppDelegate 
{
    id appDelegate = [[UIApplication sharedApplication] delegate];
    NSLog(@"app delegate's class name is %@", NSStringFromClass([appDelegate class]));
    NSLog(@"is it kind? %i", [appDelegate isKindOfClass:[CalculatorAppDelegate class]]);
    NSLog(@"is it member? %i", [appDelegate isMemberOfClass:[CalculatorAppDelegate class]]);
    NSLog(@"class == class %i", [appDelegate class] == [CalculatorAppDelegate class]);
    STAssertTrue([appDelegate isKindOfClass:[CalculatorAppDelegate class]], @"wtf");
}

What circumstances could cause NSStringFromClass() to return the correct class name, while isKindOfClass returns false?

2011-03-19 15:51:13.864 Calculator[40092:207] app delegate's class name is CalculatorAppDelegate
2011-03-19 15:51:13.864 Calculator[40092:207] is it kind? 0
2011-03-19 15:51:13.865 Calculator[40092:207] is it member? 0
2011-03-19 15:51:13.865 Calculator[40092:207] class == class 0
/Users/pohl/Developer/FoundationCalculator/CalculatorTests/CalculatorBrainTests.m:37: error: -[CalculatorBrainTests testAppDelegate] : "[appDelegate isKindOfClass:[CalculatorAppDelegate class]]" should be true. wtf
Test Case '-[CalculatorBrainTests testAppDelegate]' failed (0.002 seconds).
Wireless answered 19/3, 2011 at 18:43 Comment(0)
D
12

You haven't configured your testing target correctly. If you followed this guide for unit testing applications you should have 3 targets: Calculator, CalculatorTests and CalculatorTesting. Check the 'build phases' section in CalculatorTests. In 'Compile Sources' only the SenTestCase source files should be listed there. I guess you added the CalculatorAppDelegate.m and other files there - this would lead to duplicate assemblies of the same source files which are then linked to the same application when you build the CalculatorTesting target. That explains why your assertions fail.

EDIT: Just realized that you don't need the CalculatorTesting target in Xcode 4. Just go to Project > Edit Schemes... and make sure the unit test bundle is listed in the Test section. Then you can run unit tests with Cmd-U.

Deplore answered 19/3, 2011 at 23:21 Comment(7)
I created this project new in XCode4, and it set up the unit test target for me automatically. I have quite a few tests with many assertions that are working perfectly. It's only this one assertion that fails. Could it still be the case that my target isn't set up correctly? It looks like there is no CalculatorTesting target, so maybe the above assertion is what they refer to as an application test that needs to run in a device? On a related note, the instructions on the page you linked to don't correspond with how XCode 4 sets up targets, I think. Any tips on how to make targets in 4?Wireless
I'm pretty sure that you haven't set up your targets correctly. I'm working with Xcode 4 too and have tested your issue. In Xcode 4 you must select your normal app target in project overview and duplicate it (Cmd-D). Name it CalculatorTesting and add the OCUnit test target as dependency. Set the active scheme to be your new target and edit this scheme. Select Test on the left and add your test bundle there. Now you can run your application tests with Cmd-U.Deplore
Thank you for lending me your time. When I edit my scheme, and look under the Test section, I can see my CalculatorTests bundle is correctly listed, and it shows the test classes and methods that I have defined. To be clear: I am already able to run my unit tests with Cmd-U. I have 11 test methods containing a total of 38 assertions, and they all work correctly...except for this one. If I didn't have a correctly configured testing target, could the NSLog() messages I have pasted in my question even have been executed?Wireless
I've created a demo project in xcode 4 to demonstrate my point ( db.tt/sQLXo9P ). There are 2 schemes: The first executes the target MyTestTests1 and succeeds. The second executes the target MyTestTests2 and fails. The difference between these targets is the 'compile sources' build phase.Deplore
Thank you for your continued attention. I have downloaded your test project, and I can switch back & forth between MyTestTests1 and MyTestTests2, and I agree that you have reproduced my problem. I also agree that the only apparent difference between 1 and 2 is the presence or absence of MyTestAppDelegate.m in the "Compile Sources" under the Build Phases tab of each of the two bundles. This is both energizing and frustrating, because my CalculatorAppDelegate.m source file already appears in the "Compile Sources" of my CalculatorTests bundle. I will continue to meditate on this.Wireless
Ultimately, I resorted to creating a new project. I immediately added this assertion to the new project, and it succeeded as it should. I then copied code and resources from my old project into the new one, repeatedly running unit tests as I went, until the new project ran like the old. The problem never appeared in the new project. I then tried to scrutinize the differences between the two projects to see what could account for the difference, with no luck. I thought there might be some difference lurking in the project.pbxproj file, but it was not easy to diff. Thank you again.Wireless
Thanks for the explanation as to why adding sources manually leads to this error.Bloater
T
1

try log out whats the return value of the operation

NSLog(@"is it kind? %i", [appDelegate isKindOfClass:[CalculatorAppDelegate class]]);

and you can even test if its a member:

NSLog(@"is it member? %i", [appDelegate isMemberOfClass:[CalculatorAppDelegate class]]);

not sure if it will help but its a starting point

Tusk answered 19/3, 2011 at 19:42 Comment(7)
is it kind? 0 is it member? 0Wireless
try [appDelegate class] == [CalculatorAppDelegate class] pleaseTusk
Good idea. The output is 0 for NSLog(@"%i", [appDelegate class] == [CalculatorAppDelegate class]);Wireless
ok last approach [NSStringFromClass([appDelegate class]) isEqualToString:NSStringFromClass([CalculatorAppDelegate class])]Tusk
Ok, the output of that is 1. So this means we have two different class objects that claim the same name. Could it be that OCUnit is doing something exotic here?Wireless
my recommendation: write a bugreport at bugreport.apple.com ;)Tusk
Thank you for posting. I learned from the experience, even if we couldn't yet arrive at an answer to my question.Wireless

© 2022 - 2024 — McMap. All rights reserved.