我正在进行单元测试的一些代码需要加载资源文件。它包含以下行:
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
在应用程序中,它运行得很好,但当由单元测试框架运行时,pathForResource:
返回nil,这意味着它无法定位foo.txt
。
我已经确保foo.txt
包含在单元测试目标的Copy Bundle Resources构建阶段,那么为什么它找不到这个文件呢?
发布于 2009-12-10 15:40:26
当单元测试工具运行您的代码时,您的单元测试包是而不是主包。
即使您正在运行测试,而不是您的应用程序,您的应用程序包仍然是主包。(这可能会防止您正在测试的代码搜索错误的包。)因此,如果您将资源文件添加到单元测试包中,则在搜索主包时将找不到它。如果将上一行替换为:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"foo" ofType:@"txt"];
然后,您的代码将搜索您的单元测试类所在的包,一切都会好起来的。
发布于 2015-06-07 05:50:23
一个Swift实现:
Swift 2
let testBundle = NSBundle(forClass: self.dynamicType)
let fileURL = testBundle.URLForResource("imageName", withExtension: "png")
XCTAssertNotNil(fileURL)
Swift 3、Swift 4
let testBundle = Bundle(for: type(of: self))
let filePath = testBundle.path(forResource: "imageName", ofType: "png")
XCTAssertNotNil(filePath)
捆绑包提供了发现配置的主路径和测试路径的方法:
@testable import Example
class ExampleTests: XCTestCase {
func testExample() {
let bundleMain = Bundle.main
let bundleDoingTest = Bundle(for: type(of: self ))
let bundleBeingTested = Bundle(identifier: "com.example.Example")!
print("bundleMain.bundlePath : \(bundleMain.bundlePath)")
// …/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Agents
print("bundleDoingTest.bundlePath : \(bundleDoingTest.bundlePath)")
// …/PATH/TO/Debug/ExampleTests.xctest
print("bundleBeingTested.bundlePath : \(bundleBeingTested.bundlePath)")
// …/PATH/TO/Debug/Example.app
print("bundleMain = " + bundleMain.description) // Xcode Test Agent
print("bundleDoingTest = " + bundleDoingTest.description) // Test Case Bundle
print("bundleUnderTest = " + bundleBeingTested.description) // App Bundle
在Xcode6|7|8|9中,单元测试包路径将在Developer/Xcode/DerivedData
中,类似于...
/Users/
UserName/
Library/
Developer/
Xcode/
DerivedData/
App-qwertyuiop.../
Build/
Products/
Debug-iphonesimulator/
AppTests.xctest/
foo.txt
..。它与Developer/CoreSimulator/Devices
常规(非单元测试)包路径是分开的
/Users/
UserName/
Library/
Developer/
CoreSimulator/
Devices/
_UUID_/
data/
Containers/
Bundle/
Application/
_UUID_/
App.app/
另请注意,默认情况下,单元测试可执行文件与应用程序代码链接。但是,单元测试代码应该只在测试包中具有Target成员资格。应用程序代码在应用程序捆绑包中应该只有Target成员资格。在运行时,单元测试目标包是。
Swift包管理器(SPM) 4:
let testBundle = Bundle(for: type(of: self))
print("testBundle.bundlePath = \(testBundle.bundlePath) ")
注意:默认情况下,命令行swift test
将创建一个MyProjectPackageTests.xctest
测试包。并且,swift package generate-xcodeproj
将创建一个MyProjectTests.xctest
测试包。这些不同的测试包有不同的路径。此外,不同的测试包可能具有一些内部目录结构和__的内容差异。
在任何一种情况下,.bundlePath
和.bundleURL
都将返回当前在macOS上运行的测试包的路径。然而,Bundle
目前还没有在Ubuntu Linux上实现。
此外,命令行swift build
和swift test
目前不提供复制资源的机制。
但是,通过一些努力,可以设置在Ubuntu、macOS命令行和macOS命令行环境中使用Swift Package Manger的进程。在这里可以找到一个例子:004.4'2 SW Dev Swift Package Manager (SPM) With Resources Qref
另请参阅:Use resources in unit tests with Swift Package Manager
Swift包管理器(SwiftPM) 5.3
Swift 5.3包含带有"Status:Implemented (Swift 5.3)__“的演进提案。:-)
资源并不总是供包的客户端使用;资源的一次使用可能包括仅由单元测试所需的测试装置。这样的资源不会与库代码一起并入包的客户端,而是仅在运行包的测试时使用。
target
和testTarget
API中添加新的resources
参数,以允许显式声明资源文件。SwiftPM使用文件系统约定来确定属于包中每个目标的源文件集:具体地说,目标的源文件是位于目标的指定“目标目录”下的源文件。默认情况下,这是一个与目标同名的目录,位于"Sources“(对于常规目标)或"Tests”(对于测试目标)中,但可以在包清单中自定义此位置。
//获取DefaultSettings.plist文件的路径。let path = Bundle.module.path(forResource:"DefaultSettings",ofType:"plist") //加载可以捆绑在资产归档中的镜像。let image =UIImage(名称:"MyIcon",in: Bundle.module,compatibleWith: UITraitCollection(userInterfaceStyle:.dark)) //在编译的金属着色器库中查找顶点函数。让着色器=尝试着色器(mtlDevice.makeDefaultLibrary:Bundle.module).makeFunction(名称:"vertexShader") //加载纹理。让texture = MTKTextureLoader(device: mtlDevice).newTexture(名称:“草”,scaleFactor: 1.0,捆绑: Bundle.module,选项:选项)
示例
// swift-tools-version:5.3
import PackageDescription
targets: [
.target(
name: "CLIQuickstartLib",
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("Resources"),
]),
.testTarget(
name: "CLIQuickstartLibTests",
dependencies: [],
resources: [
// Copy directories as-is.
// Use to retain directory structure.
// Will be at top level in bundle.
.copy("Resources"),
]),
最新一期
Xcode
Bundle.module
是由SwiftPM生成的(请参阅),因此在由Xcode构建时不会出现在中。
在Xcode中,一种类似的方法是手动将Resources
引用文件夹添加到模块中,添加Xcode构建阶段copy
以将Resource
放入某个*.bundle
目录中,并为Xcode构建添加#ifdef Xcode
编译器指令以使用资源。
#if Xcode
extension Foundation.Bundle {
/// Returns resource bundle as a `Bundle`.
/// Requires Xcode copy phase to locate files into `*.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("Bundle.module could not load: \(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
发布于 2016-09-21 16:40:53
在swift 3中,语法self.dynamicType
已被弃用,请使用以下语法
let testBundle = Bundle(for: type(of: self))
let fooTxtPath = testBundle.path(forResource: "foo", ofType: "txt")
或
let fooTxtURL = testBundle.url(forResource: "foo", withExtension: "txt")
https://stackoverflow.com/questions/1879247
复制相似问题