问题描述
我试图在单元测试中使用资源文件并通过 Bundle.path
访问它,但它返回 nil.
MyProjectTests.swift 中的这个调用返回 nil:
Bundle(for: type(of: self)).path(forResource: "TestAudio", ofType: "m4a")
这是我的项目层次结构.我还尝试将 TestAudio.m4a
移动到 Resources
文件夹:
├── Package.swift├── 资料来源│ └── 我的项目│ ├── ...└── 测试└── MyProjectTests├── MyProjectTests.swift└── TestAudio.m4a
这是我的包裹描述:
//swift-tools-version:4.0导入包描述让包=包(name: "我的项目",产品: [.图书馆(name: "我的项目",目标:[我的项目"])],目标:[.目标(name: "我的项目",依赖项:[]),.testTarget(name: "MyProjectTests",依赖项:[我的项目"]),])
我使用的是 Swift 4 和 Swift Package Manager Description API 版本 4.
Swift 5.3
请参阅 Apple 文档:使用 Swift 包捆绑资源"
Swift 5.3 包括 包管理器资源 SE-0271 进化提案,带有状态:已实施(Swift 5.3)".:-)
资源并不总是供包的客户使用;资源的一种用途可能包括仅单元测试需要的测试装置.此类资源不会与库代码一起合并到包的客户端中,而只会在运行包的测试时使用.
- 在
target
和testTarget
API 中添加新的resources
参数以允许显式声明资源文件.
SwiftPM 使用文件系统约定来确定属于包中每个目标的源文件集:具体而言,目标的源文件是位于指定目标目录"下的那些文件.为目标.默认情况下,这是一个与目标同名的目录,位于Sources"目录中.(对于常规目标)或测试"(对于测试目标),但可以在包清单中自定义此位置.
//获取 DefaultSettings.plist 文件的路径.let path = Bundle.module.path(forResource: "DefaultSettings", ofType: "plist")//加载一个图像,该图像可以在一个包中的资产档案中.let image = UIImage(named: "MyIcon", in: Bundle.module, compatibleWith: UITraitCollection(userInterfaceStyle: .dark))//在已编译的 Metal 着色器库中查找顶点函数.让着色器 = 尝试 mtlDevice.makeDefaultLibrary(bundle: Bundle.module).makeFunction(name: "vertexShader")//加载纹理.let texture = MTKTextureLoader(device: mtlDevice).newTexture(name: "Grass", scaleFactor: 1.0, bundle: Bundle.module, options: options)
示例
//swift-tools-version:5.3导入包描述目标:[.目标(名称:示例",依赖项:[],资源: [//应用特定于平台的规则.//例如,图像可能会根据特定的平台规则进行优化.//如果路径是目录,则递归应用规则.//默认情况下,如果没有规则适用,将复制文件.//处理 Sources/Example/Resources 中的文件/*.process("资源"),]),.testTarget(名称:示例测试",依赖项:[示例],资源: [//按原样复制 Tests/ExampleTests/Resources 目录.//用于保留目录结构.//将在包的顶层..copy(资源"),]),
报告的问题和可能的解决方法
Xcode
Bundle.module
由 SwiftPM 生成(参见 Build/BuildPlan.swift SwiftTargetBuildDescription generateResourceAccessor()),因此不存在于 Foundation.Bundle 由 Xcode 构建时.
Xcode 中的一个类似方法是手动添加一个 Resources
引用文件夹到 Xcode 项目,添加一个 Xcode 构建阶段 copy
来放置 Resource
进入一些 *.bundle
目录,并为 Xcode 构建添加一些自定义的 #ifdef XCODE_BUILD
编译器指令以使用资源.
#if XCODE_BUILD扩展 Foundation.Bundle {///将资源包作为 `Bundle` 返回.///需要 Xcode 复制阶段才能将文件定位到 `ExecutableName.bundle`;///或 `ExecutableNameTests.bundle` 用于测试资源静态 var 模块:Bundle = {var thisModuleName = "CLIQuickstartLib";var url = Bundle.main.bundleURL对于 Bundle.allBundles 中的 bundle where bundle.bundlePath.hasSuffix(".xctest") {url = bundle.bundleURL.deletingLastPathComponent()thisModuleName = thisModuleName.appending(测试")}url = url.appendingPathComponent("\(thisModuleName).bundle")守卫让捆绑=捆绑(网址:网址)其他{致命错误(Foundation.Bundle.module 无法加载资源包:\(url.path)")}退货包}()///包含资源包的目录静态 var moduleDir: URL = {var url = Bundle.main.bundleURL对于 Bundle.allBundles 中的 bundle where bundle.bundlePath.hasSuffix(".xctest") {//移除 'ExecutableNameTests.xctest' 路径组件url = bundle.bundleURL.deletingLastPathComponent()}返回网址}()}#万一
I'm trying to use a resource file in unit tests and access it with Bundle.path
, but it returns nil.
This call in MyProjectTests.swift returns nil:
Bundle(for: type(of: self)).path(forResource: "TestAudio", ofType: "m4a")
Here is my project hierarchy. I also tried moving TestAudio.m4a
to a Resources
folder:
├── Package.swift
├── Sources
│ └── MyProject
│ ├── ...
└── Tests
└── MyProjectTests
├── MyProjectTests.swift
└── TestAudio.m4a
Here is my package description:
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "MyProject",
products: [
.library(
name: "MyProject",
targets: ["MyProject"])
],
targets: [
.target(
name: "MyProject",
dependencies: []
),
.testTarget(
name: "MyProjectTests",
dependencies: ["MyProject"]
),
]
)
I am using Swift 4 and the Swift Package Manager Description API version 4.
Swift 5.3
See Apple Documentation: "Bundling Resources with a Swift Package"
Swift 5.3 includes Package Manager Resources SE-0271 evolution proposal with "Status: Implemented (Swift 5.3)". :-)
Example
// swift-tools-version:5.3
import PackageDescription
targets: [
.target(
name: "Example",
dependencies: [],
resources: [
// Apply platform-specific rules.
// For example, images might be optimized per specific platform rule.
// If path is a directory, the rule is applied recursively.
// By default, a file will be copied if no rule applies.
// Process file in Sources/Example/Resources/*
.process("Resources"),
]),
.testTarget(
name: "ExampleTests",
dependencies: [Example],
resources: [
// Copy Tests/ExampleTests/Resources directories as-is.
// Use to retain directory structure.
// Will be at top level in bundle.
.copy("Resources"),
]),
Reported Issues & Possible Workarounds
- Swift 5.3 SPM Resources in tests uses wrong bundle path?
- Swift Package Manager - Resources in test targets
Xcode
Bundle.module
is generated by SwiftPM (see Build/BuildPlan.swift SwiftTargetBuildDescription generateResourceAccessor()) and thus not present in Foundation.Bundle when built by Xcode.
A comparable approach in Xcode would be to manually add a Resources
reference folder to the Xcode project, add an Xcode build phase copy
to put the Resource
into some *.bundle
directory, and add a some custom #ifdef XCODE_BUILD
compiler directive for the Xcode build to work with the resources.
#if XCODE_BUILD
extension Foundation.Bundle {
/// Returns resource bundle as a `Bundle`.
/// Requires Xcode copy phase to locate files into `ExecutableName.bundle`;
/// or `ExecutableNameTests.bundle` for test resources
static var module: Bundle = {
var thisModuleName = "CLIQuickstartLib"
var url = Bundle.main.bundleURL
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
url = bundle.bundleURL.deletingLastPathComponent()
thisModuleName = thisModuleName.appending("Tests")
}
url = url.appendingPathComponent("\(thisModuleName).bundle")
guard let bundle = Bundle(url: url) else {
fatalError("Foundation.Bundle.module could not load resource bundle: \(url.path)")
}
return bundle
}()
/// Directory containing resource bundle
static var moduleDir: URL = {
var url = Bundle.main.bundleURL
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
// remove 'ExecutableNameTests.xctest' path component
url = bundle.bundleURL.deletingLastPathComponent()
}
return url
}()
}
#endif
这篇关于使用 Swift Package Manager 在单元测试中使用资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!