In most of my classes that work with defaults I make the defaults object settable:
@property(retain) NSUserDefaults *defaults;
This is supposed to make testing easier:
// In a nearby test class:
- (void) setUp {
[super setUp];
NSUserDefaults *isolatedDefaults = [[NSUserDefaults alloc] init];
[someObjectBeingTested setDefaults:isolatedDefaults];
}
But now I have found out then when I create a fresh defaults object, 开发者_C百科there are already some values in it. Is that possible? I thought I could create an empty, isolated defaults object by calling -init
. Do I have a bug somewhere in the testing code, or do I really have to do something more complex (like stubbing or mocking) if I want to test my defaults-based code?
In the end I have created a simple NSUserDefaults
replacement that can be used to control the defaults environment in tests. The code is available on GitHub.
From the NSUserDefaults documentation:
init: Returns an NSUserDefaults object initialized with the defaults for the current user account.
This should normally not be empty. I am not really sure what you want to test here, since it would be a waste of time to test NSUserDefaults functionality.
But say you need some keys to be not registered yet for your test to always have the same initial point: then just remove them in setUp (and restore them later in tearDown if you want to).
Something like:
- (void) setUp {
[super setUp];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"myTestKey"];
// synchronize the change, or just use resetStandardUserDefaults:
[someObjectBeingTested setDefaults:[NSUserDefaults resetStandardUserDefaults]];
}
If you don't have a specific list of keys but need to wipe out everything, you will have to use the CoreFoundation Preferences Utilities, see CFPreferencesCopyKeyList.
We also needed to override NSUserDefaults for testing, but didn't want to change any of the application code.
So we wrote a category on NSUserDefaults
that allows us to override values returned by objectForKey:
at runtime using method swizzling.
It looks like this in Objective C:
NSLog(@"Value before: %d", [[NSUserDefaults standardUserDefaults] boolForKey:@"Example"]);
// Value before: 0
[[NSUserDefaults standardUserDefaults] overrideValue:@(YES) forKey:@"Example"];
NSLog(@"Value after: %d", [[NSUserDefaults standardUserDefaults] boolForKey:@"Example"]);
// Value after: 1
And like this in Swift:
print(UserDefaults.standard.bool(forKey: "ExampleKey")) // false
UserDefaults.standard.overrideValue(true, forKey: "ExampleKey")
print(UserDefaults.standard.bool(forKey: "ExampleKey")) // true
You can find our code on Github: https://github.com/jakob/NSUserDefaultsOverride
精彩评论