Seems like when running tests with Swift Package Manager (swift test), the working directory is the root of the project. This allows for easily loading resources from disk using a relative path (eg. ./Tests/Resources).
After evaluating different options, I wrote the following class to retrieve the path both from the test bundle, if available, or from a given path.
class Resource {
static var resourcePath = "./Tests/Resources"
let name: String
let type: String
init(name: String, type: String) {
self.name = name
self.type = type
}
var path: String {
guard let path: String = Bundle(for: Swift.type(of: self)).path(forResource: name, ofType: type) else {
let filename: String = type.isEmpty ? name : "\(name).\(type)"
return "\(Resource.resourcePath)/\(filename)"
}
return path
}
}
Resources must be added to all test targets for them to be available in the bundle. The above class could be updated to support multiple resource paths, by file extension, for instance.
Resources can be then loaded from an XCTest as needed:
let file = Resource(name: "datafile", type: "csv")
let content = try String(contentsOfFile: file)
let image = try UIImage(contentsOfFile: Resource(name: "image", type: "png"))
Extensions can be added to Resource to load the contents in different formats.
extension Resource {
var content: String? {
return try? String(contentsOfFile: path).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
var base64EncodedData: Data? {
guard let string = content, let data = Data(base64Encoded: string) else {
return nil
}
return data
}
var image: Image? {
return try? UIImage(contentsOfFile: path)
}
}
And be used as:
let json = Resource(name: "datafile", type: "json").contents
let image = Resource(name: "image", type: "jpeg").image