为 Xcode 个 SPM 测试复制资源文件
Copying Resource Files For Xcode SPM Tests
我是 Swift 包管理器的新手,但随着它集成到 Xcode 11 中,是时候尝试一下了。我在新的工作区中有一个新的应用程序和 SPM 库。我有一个带测试的工作库,并已成功将该库导入到应用程序中。
我需要使用解析 json 文件的新测试来扩展 SPM 库。我了解到不支持资源目录功能。唯一可行的方案似乎是将文件复制步骤添加到库构建过程中,以便可执行文件可以发现资源文件。
我可以弄清楚如何从命令行执行此操作,但不能使用 Xcode 运行 构建和测试。没有 Copy Bundle Resources,swift 包的构建阶段。事实上 一切 似乎都被 Xcode 隐藏了。
我在 SPM 中查找 Makefile 类型的文件,这些文件允许我编辑默认的命令行操作,从而规避 Xcode;但是我没有看到它们。
有什么方法可以 interact/control Xcode 11 如何构建 SPM 目标,以便我可以将非代码文件复制到测试目标?
成功了!!!
struct Resource {
let name: String
let type: String
let url: URL
init(name: String, type: String, sourceFile: StaticString = #file) throws {
self.name = name
self.type = type
// The following assumes that your test source files are all in the same directory, and the resources are one directory down and over
// <Some folder>
// - Resources
// - <resource files>
// - <Some test source folder>
// - <test case files>
let testCaseURL = URL(fileURLWithPath: "\(sourceFile)", isDirectory: false)
let testsFolderURL = testCaseURL.deletingLastPathComponent()
let resourcesFolderURL = testsFolderURL.deletingLastPathComponent().appendingPathComponent("Resources", isDirectory: true)
self.url = resourcesFolderURL.appendingPathComponent("\(name).\(type)", isDirectory: false)
}
}
用法:
final class SPMTestDataTests: XCTestCase {
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
XCTAssertEqual(SPMTestData().text, "Hello, World!")
let file = try Resource(name: "image", type: "png")
let image = UIImage(contentsOfFile: file.url.path)
print(image)
}
}
我找到了使用的关键 #file
here
这是提供对测试资源的访问的另一种解决方法。希望 OP 问题的答案即将到来。
使用下面的代码,创建一个扩展以允许调用者创建 URL 来测试这样的资源。
let url = URL(forResource: "payload", type: "json")
此代码要求所有资源文件都位于测试目标正下方名为 "Resources" 的平面目录中。
// MARK: - ./Resources/ Workaround
// URL of the directory containing non-code, test resource fi;es.
//
// It is required that a directory named "Resources" be contained immediately below the test target.
// Root
// Package.swift
// Tests
// (target)
// Resources
//
fileprivate let _resources: URL = {
func packageRoot(of file: String) -> URL? {
func isPackageRoot(_ url: URL) -> Bool {
let filename = url.appendingPathComponent("Package.swift", isDirectory: false)
return FileManager.default.fileExists(atPath: filename.path)
}
var url = URL(fileURLWithPath: file, isDirectory: false)
repeat {
url = url.deletingLastPathComponent()
if url.pathComponents.count <= 1 {
return nil
}
} while !isPackageRoot(url)
return url
}
guard let root = packageRoot(of: #file) else {
fatalError("\(#file) must be contained in a Swift Package Manager project.")
}
let fileComponents = URL(fileURLWithPath: #file, isDirectory: false).pathComponents
let rootComponenets = root.pathComponents
let trailingComponents = Array(fileComponents.dropFirst(rootComponenets.count))
let resourceComponents = rootComponenets + trailingComponents[0...1] + ["Resources"]
return URL(fileURLWithPath: resourceComponents.joined(separator: "/"), isDirectory: true)
}()
extension URL {
init(forResource name: String, type: String) {
let url = _resources.appendingPathComponent("\(name).\(type)", isDirectory: false)
self = url
}
}
我是 Swift 包管理器的新手,但随着它集成到 Xcode 11 中,是时候尝试一下了。我在新的工作区中有一个新的应用程序和 SPM 库。我有一个带测试的工作库,并已成功将该库导入到应用程序中。
我需要使用解析 json 文件的新测试来扩展 SPM 库。我了解到不支持资源目录功能。唯一可行的方案似乎是将文件复制步骤添加到库构建过程中,以便可执行文件可以发现资源文件。
我可以弄清楚如何从命令行执行此操作,但不能使用 Xcode 运行 构建和测试。没有 Copy Bundle Resources,swift 包的构建阶段。事实上 一切 似乎都被 Xcode 隐藏了。
我在 SPM 中查找 Makefile 类型的文件,这些文件允许我编辑默认的命令行操作,从而规避 Xcode;但是我没有看到它们。
有什么方法可以 interact/control Xcode 11 如何构建 SPM 目标,以便我可以将非代码文件复制到测试目标?
成功了!!!
struct Resource {
let name: String
let type: String
let url: URL
init(name: String, type: String, sourceFile: StaticString = #file) throws {
self.name = name
self.type = type
// The following assumes that your test source files are all in the same directory, and the resources are one directory down and over
// <Some folder>
// - Resources
// - <resource files>
// - <Some test source folder>
// - <test case files>
let testCaseURL = URL(fileURLWithPath: "\(sourceFile)", isDirectory: false)
let testsFolderURL = testCaseURL.deletingLastPathComponent()
let resourcesFolderURL = testsFolderURL.deletingLastPathComponent().appendingPathComponent("Resources", isDirectory: true)
self.url = resourcesFolderURL.appendingPathComponent("\(name).\(type)", isDirectory: false)
}
}
用法:
final class SPMTestDataTests: XCTestCase {
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
XCTAssertEqual(SPMTestData().text, "Hello, World!")
let file = try Resource(name: "image", type: "png")
let image = UIImage(contentsOfFile: file.url.path)
print(image)
}
}
我找到了使用的关键 #file
here
这是提供对测试资源的访问的另一种解决方法。希望 OP 问题的答案即将到来。
使用下面的代码,创建一个扩展以允许调用者创建 URL 来测试这样的资源。
let url = URL(forResource: "payload", type: "json")
此代码要求所有资源文件都位于测试目标正下方名为 "Resources" 的平面目录中。
// MARK: - ./Resources/ Workaround
// URL of the directory containing non-code, test resource fi;es.
//
// It is required that a directory named "Resources" be contained immediately below the test target.
// Root
// Package.swift
// Tests
// (target)
// Resources
//
fileprivate let _resources: URL = {
func packageRoot(of file: String) -> URL? {
func isPackageRoot(_ url: URL) -> Bool {
let filename = url.appendingPathComponent("Package.swift", isDirectory: false)
return FileManager.default.fileExists(atPath: filename.path)
}
var url = URL(fileURLWithPath: file, isDirectory: false)
repeat {
url = url.deletingLastPathComponent()
if url.pathComponents.count <= 1 {
return nil
}
} while !isPackageRoot(url)
return url
}
guard let root = packageRoot(of: #file) else {
fatalError("\(#file) must be contained in a Swift Package Manager project.")
}
let fileComponents = URL(fileURLWithPath: #file, isDirectory: false).pathComponents
let rootComponenets = root.pathComponents
let trailingComponents = Array(fileComponents.dropFirst(rootComponenets.count))
let resourceComponents = rootComponenets + trailingComponents[0...1] + ["Resources"]
return URL(fileURLWithPath: resourceComponents.joined(separator: "/"), isDirectory: true)
}()
extension URL {
init(forResource name: String, type: String) {
let url = _resources.appendingPathComponent("\(name).\(type)", isDirectory: false)
self = url
}
}