我为NSFileCoordinator API编写了一个异步接口。
struct FileCoordinator {
private init() {}
static let shared = FileCoordinator()
func coordinateWriting(of data: Data, to url: URL) async throws {
try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation<Void, Error>) in
var error: NSError?
func handleWriting(newURL: URL) {
do {
try data.write(to: newURL, options: .atomic)
continuation.resume()
} catch {
continuation.resume(throwing: error)
// This is the line that I have been able to test by making this method write to dev null.
}
}
NSFileCoordinator().coordinate(writingItemAt: url, options: .forReplacing, error: &error, byAccessor: handleWriting)
// Developer documentation: If a file presenter encounters an error while preparing for this write operation, that error is returned in this parameter and the block in the writer parameter is NOT executed.
// So in theory, we shouldn’t resume our continuation more than once.
if let error = error {
continuation.resume(throwing: error)
// This is the line that I have not been able to test.
}
})
}
}现在我正在为这个逻辑编写单元测试。
final class FileCoordinatorTests: XCTestCase {
static let testDirectoryURL: URL = {
var url = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("EssentialHelpersTests", isDirectory: true)
url = url.appendingPathComponent("FileCoordinatorTests", isDirectory: true)
return url
}()
override func setUpWithError() throws {
// Create a directory for this test class.
try FileManager.default.createDirectory(at: Self.testDirectoryURL, withIntermediateDirectories: true)
}
override func tearDownWithError() throws {
// Delete a directory after each test.
try FileManager.default.removeItem(at: Self.testDirectoryURL)
}
func test_FileWritingCoordination() async throws {
// given
let data = Data("testData".utf8)
let fileName = UUID().uuidString
let url = Self.testDirectoryURL.appendingPathComponent(fileName)
// when
try await FileCoordinator.shared.coordinateWriting(of: data, to: url)
// then
XCTAssertTrue(FileManager.default.fileExists(atPath: url.path))
}
func test_FileWritingCoordinationWithError() async {
// given
let data = Data("testData".utf8)
let urlWeDoNotHavePermissionToWriteTo = URL(fileURLWithPath: "/dev/null")
var error: Error?
// when
do {
try await FileCoordinator.shared.coordinateWriting(of: data, to: urlWeDoNotHavePermissionToWriteTo)
} catch let err {
error = err
}
// then
XCTAssertNotNil(error)
}
}我似乎想不出一种方法来模拟NSFileCoordinator的错误条件,所以它会将一个错误对象分配给我们提供的指针。文档称,当文件演示者在准备写操作时遇到问题时,会创建错误。但我一开始就不使用文件演示器。我正在使用这个API来验证我的代码,以防将来添加iCloud支持。
文档说,如果我们在协调器上调用cancel()方法,就会生成错误。但是,在我的代码上下文中,应该在哪里调用该方法呢?我试着在调用coordinate(writingItemAt:options:writingItemAt:options:error:byAccessor:)之后调用它,但这没有任何效果。
我担心如果出现错误,我的代码结构可能会导致连续误用(恢复两次)。即使如果存在错误(根据文档),处理文件操作的块也不会执行,但我无法确认这一点。
发布于 2022-07-26 10:50:50
您不应该直接对NSFileCoordinator进行单元测试。相反,您应该创建一个NSFileCoordinator符合的协议,并为单元测试注入一个符合相同协议的模拟。这将允许您在单元测试中控制所需的依赖行为,并测试FileCoordinator在特定依赖条件下的正确行为。
您也不应该使用单例,因为这会使依赖注入和单元测试更加困难。
https://stackoverflow.com/questions/73121638
复制相似问题