The singleton-done-right example from my article on the subject is now in a Mercurial repository on Bitbucket. The repository includes not only the class (which I’ve put in the public domain), but also a test suite for some of the test cases listed in the post.
There are some adventurous testing techniques at work here.
First, since we don’t want multiple test runs to use the same singleton instance, the test cases actually run in subprocesses. Each test method is prefaced with this code:
if (!isInSubprocess) {
[self runTestInSubprocess:_cmd];
return;
}
That method calls fork
.
In the child process, the test case sets isInSubprocess
to YES
, then tells itself to perform that selector; this gets execution back to the test method, which checks the variable again, finds that it’s true this time, and continues on with the test.
The parent process calls waitpid
and checks the result; if the child failed, the parent propagates the failure. If the child crashed (got a signal), the parent raises the same signal; if the child simply exited abnormally, then the parent exits with the same status.
Second, there’s test case #6:
- If
[super init]
returns a different object, alloc
/init
won’t break.
That one is hard to test, because PRHEmptySingleton’s superclass is NSObject, and -[NSObject init]
doesn’t return a different object. (Not in any version of Foundation I’ve ever encountered, anyway.)
So, the first step is to create an intermediate class that does return a different object. But that doesn’t help as long as PRHEmptySingleton is still a direct subclass of NSObject.
The simple solution would be to just change PRHEmptySingleton’s superclass, but that reduces the purity of the testing: A test should be able to work without modifying the code under test, and any changes to the code under test should be permanent changes that aren’t only to enable the test; you should be able to explain the changes as if the test case did not exist.
So what I did was to import everything in my prefix header, even more than I usually do, and then import my intermediate class’s header, and then use the preprocessor to ensure that any direct subclasses of NSObject declared elsewhere are declared as subclasses of the intermediate class. Thus, the prefix header causes PRHEmptySingleton to be a subclass of the intermediate class with no changes to PRHEmptySingleton’s header. It’s a bit of a hack, and doing this sort of thing could potentially cause problems in a larger program or test suite, but in this context, it works.
With that, five of the six test cases in the original post are now covered (I’m not sure how to cover #3 without changing PRHEmptySingleton.[hm]), and the code is under version control, so you can subscribe to and track changes.
Please use singletons responsibly.