我单元测试的一些代码需要加载资源文件。它包含以下行:
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
在应用程序中它运行得很好,但是当单元测试框架pathForResource:
运行时,返回nil,意味着找不到foo.txt
。
我已确保foo.txt
包含在单元测试目标的复制包资源构建阶段中,为什么找不到该文件?
答案 0 :(得分:299)
当单元测试工具运行您的代码时,您的单元测试包不主包。
即使您正在运行测试,而不是您的应用程序,您的应用程序包仍然是主要的捆绑包。 (据推测,这可以防止您正在测试的代码搜索错误的包。)因此,如果您将资源文件添加到单元测试包中,则在搜索主包时将无法找到它。如果您将以上行替换为:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"foo" ofType:@"txt"];
然后你的代码将搜索你的单元测试类所在的包,一切都会好的。
答案 1 :(得分:67)
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)
Bundle提供了发现配置的主路径和测试路径的方法:
@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
在Xcode 6 | 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/
另请注意,单元测试可执行文件默认情况下与应用程序代码链接。但是,单元测试代码应仅在测试包中具有目标成员资格。应用程序代码应仅在应用程序包中具有目标成员身份。在运行时,单元测试目标包为injected into the application bundle for execution。
Swift Package Manager(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上运行的测试包的路径。但是,目前还没有为Ubuntu Linux实现Bundle
。
此外,命令行swift build
和swift test
目前不提供复制资源的机制。
但是,通过一些努力,可以设置使用Swift包管理器的进程以及macOS Xcode,macOS命令行和Ubuntu命令行环境中的资源。可以在此处找到一个示例:004.4'2 SW Dev Swift Package Manager (SPM) With Resources Qref
另请参阅:Use resources in unit tests with Swift Package Manager
Swift Package Manager(SPM)4.2
Swift Package Manager PackageDescription 4.2引入了对local dependencies的支持。
本地依赖项是磁盘上的程序包,可以使用它们的路径直接引用。仅在根包中允许本地依赖,并且它们将覆盖包图中具有相同名称的所有依赖项。
注意:我希望,但尚未测试,SPM 4.2应该可以使用以下内容:
// swift-tools-version:4.2
import PackageDescription
let package = Package(
name: "MyPackageTestResources",
dependencies: [
.package(path: "../test-resources"),
],
targets: [
// ...
.testTarget(
name: "MyPackageTests",
dependencies: ["MyPackage", "MyPackageTestResources"]
),
]
)
答案 2 :(得分:13)
使用swift 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")
答案 3 :(得分:4)
答案 4 :(得分:1)
答案 5 :(得分:0)