iosswiftnsuserdefaultsxctestuserdefaults

Removing UserDefaults-generated plist file when running unit tests


I'm testing a class that depends on an instance of UserDefaults. In following this sample code found here, I create and setup the instance like so:

override func setUp() {
    super.setUp()
    defaults = UserDefaults(suiteName: #file)
    defaults.removePersistentDomain(forName: #file)
}

After running the tests, a plist file is created within the same directory as this test class. If my testing file is named TestFile.swift the plist is given the name TestFile.swift.plist. I'm pretty sure this is generated at the call of the suiteName: initializer above. My question is: how do I remove this file once the tests have completed? I've tried making calls to removeSuite(named: #file), removeVolatileDomain(forName: #file), and removePersistentDomain(forName: #file) within the test's tearDown method but no luck. Calling synchronize() didn't seem to help either.


Solution

  • Ideally, instead of passing in an actual instance of UserDefaults, you would instead pass a different type of object that doesn't require that cleanup. A simple example would be something like:

    class UnitTestDefaults: UserDefaults {
    
        private var values = [String: Any]()
    
        // override only the functions you need, e.g.
    
        override func object(forKey defaultName: String) -> Any? {
            values[defaultName]
        }
    
        override func set(_ value: Any?, forKey defaultName: String) {
            values[defaultName] = value
        }
    
    }
    

    Alternatively, you could define a new protocol and have the object you're testing depend on an instance of that protocol instead of UserDefaults. This would be considered more proper practice for unit testing, and would also solve your problem if simply creating an instance of UserDefaults, even a subclass that doesn't define a custom suite, still has unwanted side effects.

    protocol DefaultsStorage {
        func object(forKey defaultName: String) -> Any?
        func set(_ value: Any?, forKey defaultName: String)
    }
    
    class UnitTestDefaults: DefaultsStorage {
        // ...
    }