I'm trying to be proactive about writing an iOS app in a test-driven manner. However, I'm stumped as to how to test a method that is to interact with real system files. For those familiar with iOS, I'm writing to the NSUserDefaults
file.
Here's my set-up: I have a singleton called AppState
that I am using to record when data downloaded from the Internet was last synced locally. Other classes can update the dateLastSynced
property of AppState
. I am overriding the setDateLastSynced
method to save that update to the system so it's persisted between opening and closing the app:
- (void)setDateLastSynced:(NSDate *)dateLastSynced
{
// update instance variable with new value
_dateLastSynced = dateLastSynced;
// store change to NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:dateLastSynced forKey:APP_STATE_DATE_LAST_SYNCED_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I'd like to write a test that verifies that setDateLastSynced
is working correctly – but I'm not sure how to do that without overwriting data already saved to the system in NSUserDefaults
.
Best Answer
You have to mock or emulate, and you have two very good reasons to do so.
You have to mock or emulate whatever parts of the system your code interacts with, precisely because:
You don't want your tests to depend on something as uncontrollable as the system.
Yes, your emulation will not be perfect; yes, you will in fact be modelling your knowledge and assumptions about how the system works; and that's perfectly fine. You are essentially taking a spec and restating it in code: nothing wrong with that. The spec in this case is written by the creators of the operating system and not by you.
You have to mock or emulate everything that deals with time, even your own stuff.