NSUserDefaults is where you can store persistent app settings, as an alternative to storing them in some kind of in-app database. You shouldn’t try to store actual application data in
NSUserDefaults, but they are useful for capturing “small” settings like environment URLs and so on.
If you’re using
NSUserDefaults, you should be testing
NSUserDefaults. Here’s how.
One of the most common testing scenarios is that you want to store some value in the defaults store so that it can be read out later. This is the process for testing that your code actually does so. Assume that we’ve got a class called
myClass which has an
IBAction method called
didTapUpdateSettingsButton which will write out a specific value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Walking through this, we first create variables for the key and object that we are expecting the
didTapUpdateSettings method to attempt to write to the defaults store.
Next, we create a mock object which will stand in for the instance of the
NSUserDefaults class that would normally be returned by the
[NSUserDefaults standardUserDefaults] method. We’re going to use this mock to catch and test the messages that our code under test sends. It’s created as a null mock here so that it won’t complain if it gets messages other than the ones that we send it.
Then we need to stub the
standardUserDefaults method of the real, live
NSUserDefaults class, and return our mock instance instead of the real thing.
Once we’ve stubbed out
NSUserDefaults, we can now set some expectations about what our mock
standardUserDefaults will receive. There are two things we’re interested in – firstly, that it receives the
setObject:forKey message with the values that we expect. If that message isn’t received, or the values differ from what we expect, then there’s something wrong with the code that we’re writing.
Secondly, we’re also setting an expectation that the mock receives the
synchronize message. This saves the values that we’ve updated – not sending a
synchronize message to
NSUserDefaults is a common mistake, and hard to diagnost – with a specific test, we’ll catch any situations where we forget that final step.
Finally, we invoke the code under test by “tapping” the button (or in this case, calling the
IBAction method that the button is wired up to).
If you’re taking a test-first approach to writing the code, obviously this will initially fail. But by fixing each failing assertion in turn, you’ll end up with a method that both writes and saves the correct values.