7

I'm trying to be good and writing unit tests for my classes, but I have some classes that deal with preferences and checking for first run. The test functions I've written work fine the first time they're run, but subsequent tests fail because the preferences already have a value. Setting the object to nil results in Optional<Any> value which does not equal nil when fetched with UserDefaults.standard.object(forKey:).

I have also tried UserDefaults.standard.removeObject(forKey:) and UserDefaults.standard.synchronize() and UserDefaults.resetStandardUserDefaults(). Nothing seems to work and Bundle.main.bundleIdentifier value is nil which means I can't use UserDefaults.standard.removePersistentDomain(forName:) either!

I have discovered the preferences are being saved in: /Users/<name>/Library/Developer/CoreSimulator/Devices/<UUID>/data/Library/Preferences/xctest.plist and I've tried using the string "xctest" in removePersistentDomain and that too does not work.

I can set the value to some other value and check against that, but I really hate to junk up my code to test for a situation only ever present in the XCTest (I already have a function that is dedicated to doing this reset because the UserDefaults within the XCTest cases are different than the UserDefaults within the code!), so how on earth can I reset the standard UserDefaults in an XCTestCase?

Kudit
  • 4,212
  • 2
  • 26
  • 32

1 Answers1

0

Without a concrete example, it's difficult to solve your problem, but here's what has worked for me.

When I need UserDefaults within a class, I define the class initializer to take a UserDefaults instance, like this:

class Foo {

    private let userDefaults: UserDefaults

    init(userDefaults: UserDefaults = .standard) {
        self.userDefaults = userDefaults
    }
}

Then I can inject a throw-away UserDefaults instance for testing like this:

class FooTests: XCTestCase {

    let userDefaultsSuiteName = "FooTests"
    var userDefaults: UserDefaults!
    var foo: Foo!

    override func setUpWithError() throws {
        userDefaults = UserDefaults(suiteName: userDefaultsSuiteName)
        foo = Foo(userDefaults: userDefaults)
    }

    override func tearDownWithError() throws {
        userDefaults.removePersistentDomain(forName: userDefaultsSuiteName)
    }
}