在Swift单元测试中创建临时文件

时间:2019-05-03 19:39:43

标签: swift xcode file unit-testing

我想编写一个读取/写入文件的单元测试,所以我制作了一个帮助器函数来制作一个临时文件(改编自Apple's docs):

func mkTmp() -> URL {
    let fileManager = FileManager.default
    let directory = fileManager.temporaryDirectory
    let filename = UUID().uuidString
    let fileURL = directory.appendingPathComponent(filename)

    addTeardownBlock {
        do {
            if fileManager.fileExists(atPath: fileURL.path) {
                try fileManager.removeItem(at: fileURL)
                XCTAssertFalse(fileManager.fileExists(atPath: fileURL.path))
            }
        } catch {
            XCTFail("Error while deleting temporary file: \(error)")
        }
    }

    do {
        try fileManager.createDirectory(at: fileURL, withIntermediateDirectories: true)
        // FIXME: this always fails for some reason
        XCTAssertTrue(fileManager.createFile(atPath: fileURL.path, contents: "test".data(using: .utf8)))
    } catch {
        XCTFail("Error while making temp dir: \(error)")
    }
    return fileURL
}

问题是createFile行接近结尾。它总是失败!单元测试对临时目录没有写权限吗?

如果我忽略该失败并尝试写入文件

let file = try FileHandle(forWritingTo: mkTmp())
defer { file.closeFile() }
file.write(data)

尝试打开文件句柄时收到错误消息,指出文件不存在。

我需要在单元测试的某处设置文件写入权限吗?

2 个答案:

答案 0 :(得分:1)

无法创建文件,因为该目录/ URL由fileManager.createDirectory(at: fileURL, ...)创建,因此该目录已经存在。您可以通过在调用fileURL之后添加以下断言来检查fileManager.createDirectory(at: fileURL, ...)上存在的内容来确认这一点:

try fileManager.createDirectory(at: fileURL, withIntermediateDirectories: true)

var isDirectory: ObjCBool = false
XCTAssertTrue(fileManager.fileExists(atPath: fileURL.path, isDirectory: &isDirectory))
XCTAssertTrue(isDirectory.boolValue)

要能够在fileURL上创建文件,您只想创建其父目录:

try fileManager.createDirectory(at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true)

答案 1 :(得分:-1)

在单元测试中创建文件时,会破坏F.I.R.S.T.原则。相反,我将以这种方式模拟文件系统的行为:

protocol FileSystemProtocol {
    func createDirectory(_ url: URL) throws
    func deleteDirectory(_ url: URL) throws
    // readFile, writeFile etc
}

class FileSystemMock: FileSystemProtocol {
    var directoryUrls = Set<URL>()

    func createDirectory(_ url: URL) throws {
        self.directoryUrls.insert(url)
    }

    func deleteDirectory(_ url: URL) throws {
        self.directoryUrls.remove(url)
    }
}

// setup
let fileSystemMock = FileSystemMock()
let testableObject = TestableClass(fileSystemMock)

// test testableMethod
XCTAssertNoThrow(try testableObject.testableMethod())
XCTAssert(fileSystemMock.directoryUrls.count == 1)