在运行单元测试时删除UserDefaults生成的plist文件

8

我正在测试依赖UserDefaults实例的类。按照发现在 这里 的示例代码,我创建并设置实例:

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

运行测试后,在与此测试类相同的目录中创建一个plist文件。 如果我的测试文件名为TestFile.swift,则该plist文件的名称为TestFile.swift.plist。 我很确定这是在上面的suiteName:初始化器调用时生成的。我的问题是:如何在测试完成后删除此文件? 我尝试在测试的tearDown方法中调用removeSuite(named:#file)removeVolatileDomain(forName:#file)removePersistentDomain(forName:#file),但没有成功。调用synchronize()似乎也没有帮助。


就像任何属性列表的值一样,您需要将其设置为nil才能将其清除。请参见下面的完整答案。 - SmileBot
2个回答

3
理想情况下,不应该传入一个真实的UserDefaults实例,而是传递一种不需要清理的不同类型的对象。一个简单的例子可能是这样的:
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
    }

}

另外,您可以定义一个新协议,并使您要测试的对象依赖于该协议的实例,而不是依赖于 UserDefaults。这将被认为是单元测试的更好做法,如果仅创建一个UserDefaults实例(即使是未定义自定义套件的子类),仍会产生意想不到的副作用,也可以解决您的问题。

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

class UnitTestDefaults: DefaultsStorage {
    // ...
}

1

这是一个测试目标,因此提交它不应该成为一个问题。一旦提交了一次,就不需要再担心它了。

如果您想确保在运行之间保留清洁状态,那么在拆卸时将其设置为nil可能是个好主意。

override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        try super.tearDownWithError()
        mockDefaults()
    }

这里mockDefaults()调用传递了一个值为nil的参数,产生了一个包含如下内容的属性列表:

<plist version="1.0">
<dict/>
</plist>

它调用:

private extension FakeInrangeTests {
    func mockDefaults(value: Bool? = nil) -> UserDefaults {
        MockDefaults.makeInrange(#file, value)
    }
}

调用哪个函数:

struct MockDefaults {
    static func make(
        _ file: String,
        value: Any? = nil,
        key: String?
    ) -> UserDefaults {
        let userDefaults = UserDefaults(suiteName: file)!
        userDefaults.removePersistentDomain(forName: file)
        if let value = value, let key = key {
            userDefaults.setValue(value, forKey: key)
        }
        return userDefaults
    }
}

extension MockDefaults {
    static func makeInrange(
        _ file: String,
        _ value: Bool?
    ) -> UserDefaults {
        MockDefaults.make(
            file,
            value: value,
            key: UserDefaults.fakeInrangeKey
        )
    }
}

你可以将它添加到你的 .gitignore 文件中。

*.swift.plist

您可以通过运行以下命令来确认 plist 文件未被删除

var defaults: UserDefaults!

    override func setUpWithError() throws {
        continueAfterFailure = false
        try super.setUpWithError()
        defaults = UserDefaults(suiteName: #file)
        defaults.setValue(12, forKey: "myKey")
    }
    
    override func tearDownWithError() throws {
        try super.tearDownWithError()
        defaults.removePersistentDomain(forName: #file)
    }

不确定为什么清理plist文件会如此复杂。在tearDown中使用userDefaults.removePersistentDomain(forName:file)调用是否就足够了?或者我有什么疏漏吗? - Werewolf
我更新了我的帖子,并附上了一些示例代码,您可以轻松运行以测试自己。@Werewolf - SmileBot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接