views:

308

answers:

1

Is there any way to ensure that a class posts a particular NSNotification?

(I have a set of classes, and I would like to enforce at compile-time (if possible) that the class posts a required NSNotification).

Alternatively, if that is not possible, is there any workaround?

+3  A: 

It's fundamentally impossible to predict at compile time what will happen at run time. The closest you can get is static analysis, but even that can't predict anything that happens outside of your own code, such as inside Foundation.

You can, however, do this with unit tests, since the test runner actually runs the code under test.

You'll need to create a test bundle target, if you haven't already. Your target will use SenTestingKit to run your tests, which you create. (On the iPhone, you'll also need Google Toolbox for, uh, Mac. They have a handy tutorial on using GTM for iPhone tests.)

You'll create a SenTestCase subclass to test whether your real object posts a notification. It'll look something like this:

@interface FrobnitzerNotificationsTest: SenTestCase
{
 BOOL frobnitzerDidCalibrate;
}

- (void) frobnitzerDidCalibrate:(NSNotification *)notification;

@end

@implementation FrobnitzerNotificationsTest

- (void) testFrobnitzerCalibratePostsNotification {
 Frobnitzer *frobnitzer = …;
 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

 [nc addObserver:self
  selector:@selector(frobnitzerDidCalibrate:)
  name:FrobnitzerDidCalibrate
  object:frobnitzer];

 frobnitzerDidCalibrate = NO;

 //This should post a notification named FrobnitzerDidCalibrate with the receiver as the object.
 [frobnitzer calibrate];
 //If it did, our notification handler set frobnitzerDidCalibrate to YES (see below).

 [nc removeObserver:self
  name:FrobnitzerDidCalibrate
  object:frobnitzer];

 STAssertTrue(frobnitzerDidCalibrate, @"Frobnitzer did not post a notification when we told it to calibrate");
}

- (void) frobnitzerDidCalibrate:(NSNotification *)notification {
 frobnitzerDidCalibrate = YES;
}

@end

You'll need one instance variable and one notification-handler method for every notification you want to test for, and one test method for every method you want to test for notifications.

Also, if using GTM, you must substitute GTMSenTestCase for SenTestCase above.

Peter Hosey
I dare say I'm missing something but Apple has several classes (NSView, ABPeoplePickerView) that promise to post notifications when a certain event takes place. I can't quite see how test cases will enforce that these notifications are posted, as requested by Nocturne.
Elise van Looij
If the notification (or any other thing under test, such as drawing into an image before Snow Leopard) requires a window server connection to happen, then you won't be able to test it in the command-line tool provided by OCUnit. You'd need to implement something that runs the tests in an app. In the test case method, the code would be the same: Add observer, set instance variable to `NO`, cause the notification,( set the instance variable to `YES` in the notification method,) remove observer, assert that the instance variable is `YES`.
Peter Hosey