xcode 为什么单元测试中的代码不能找到捆绑资源?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1879247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't code inside unit tests find bundle resources?
提问by benzado
Some code I am unit testing needs to load a resource file. It contains the following line:
我正在单元测试的一些代码需要加载资源文件。它包含以下行:
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
In the app it runs just fine, but when run by the unit testing framework pathForResource:
returns nil, meaning it could not locate foo.txt
.
在应用程序中它运行得很好,但是当由单元测试框架运行时pathForResource:
返回 nil,这意味着它无法找到foo.txt
.
I've made sure that foo.txt
is included in the Copy Bundle Resourcesbuild phase of the unit test target, so why can't it find the file?
我已经确保它foo.txt
包含在单元测试目标的复制捆绑资源构建阶段,那么为什么找不到该文件呢?
回答by benzado
When the unit test harness runs your code, your unit test bundle is NOTthe main bundle.
当单元测试工具运行您的代码时,您的单元测试包不是主包。
Even though you are running tests, not your application, your application bundle is still the main bundle. (Presumably, this prevents the code you are testing from searching the wrong bundle.) Thus, if you add a resource file to the unit test bundle, you won't find it if search the main bundle. If you replace the above line with:
即使您正在运行测试,而不是您的应用程序,您的应用程序包仍然是主包。(大概,这可以防止您正在测试的代码搜索错误的包。)因此,如果您将资源文件添加到单元测试包中,则在搜索主包时将找不到它。如果将上面的行替换为:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"foo" ofType:@"txt"];
Then your code will search the bundle that your unit test class is in, and everything will be fine.
然后您的代码将搜索您的单元测试类所在的包,一切都会好起来的。
回答by l --marc l
A Swift implementation:
一个 Swift 实现:
Swift 2
斯威夫特 2
let testBundle = NSBundle(forClass: self.dynamicType)
let fileURL = testBundle.URLForResource("imageName", withExtension: "png")
XCTAssertNotNil(fileURL)
Swift 3, Swift 4
斯威夫特 3、斯威夫特 4
let testBundle = Bundle(for: type(of: self))
let filePath = testBundle.path(forResource: "imageName", ofType: "png")
XCTAssertNotNil(filePath)
Bundle provides ways to discover the main and test paths for your configuration:
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
In Xcode 6|7|8|9, a unit-test bundle pathwill be in Developer/Xcode/DerivedData
something like ...
在 Xcode 6|7|8|9 中,单元测试包路径将Developer/Xcode/DerivedData
类似于...
/Users/
UserName/
Library/
Developer/
Xcode/
DerivedData/
App-qwertyuiop.../
Build/
Products/
Debug-iphonesimulator/
AppTests.xctest/
foo.txt
... which is separate from the Developer/CoreSimulator/Devices
regular (non-unit-test) bundle path:
...与Developer/CoreSimulator/Devices
常规(非单元测试)包路径分开:
/Users/
UserName/
Library/
Developer/
CoreSimulator/
Devices/
_UUID_/
data/
Containers/
Bundle/
Application/
_UUID_/
App.app/
Also note the unit test executable is, by default, linked with the application code. However, the unit test code should only have Target Membership in just the test bundle. The application code should only have Target Membership in the application bundle. At runtime, the unit test target bundle is injected into the application bundle for execution.
另请注意,默认情况下,单元测试可执行文件与应用程序代码链接。但是,单元测试代码应该只在测试包中具有 Target Membership。应用程序代码在应用程序包中应该只具有目标成员资格。在运行时,单元测试目标包被注入到应用程序包中执行。
Swift Package Manager (SPM) 4:
Swift 包管理器 (SPM) 4:
let testBundle = Bundle(for: type(of: self))
print("testBundle.bundlePath = \(testBundle.bundlePath) ")
Note: By default, the command line swift test
will create a MyProjectPackageTests.xctest
test bundle. And, the swift package generate-xcodeproj
will create a MyProjectTests.xctest
test bundle. These different test bundles have different paths. Also, the different test bundles may have some internal directory structure and content differences.
注意:默认情况下,命令行将swift test
创建一个MyProjectPackageTests.xctest
测试包。并且,swift package generate-xcodeproj
将创建一个MyProjectTests.xctest
测试包。这些不同的测试包有不同的路径。此外,不同的测试包可能有一些内部目录结构和内容差异。
In either case, the .bundlePath
and .bundleURL
will return the path of test bundle currently being run on macOS. However, Bundle
is not currently implemented for Ubuntu Linux.
在任何一种情况下,.bundlePath
和.bundleURL
都将返回当前在 macOS 上运行的测试包的路径。但是,Bundle
目前尚未为 Ubuntu Linux 实现。
Also, command line swift build
and swift test
do not currently provide a mechanism for copying resources.
此外,命令行swift build
和swift test
当前不提供复制资源的机制。
However, with some effort, it is possible to set up processes for using the Swift Package Manger with resources in the macOS Xcode, macOS command line, and Ubuntu command line environments. One example can be found here: 004.4'2 SW Dev Swift Package Manager (SPM) With Resources Qref
但是,通过一些努力,可以在 macOS Xcode、macOS 命令行和 Ubuntu 命令行环境中设置使用 Swift Package Manger 和资源的过程。可以在此处找到一个示例:004.4'2 SW Dev Swift Package Manager (SPM) With Resources Qref
See also: Use resources in unit tests with Swift Package Manager
另请参阅:使用 Swift Package Manager 在单元测试中使用资源
Swift Package Manager (SPM) 4.2
Swift 包管理器 (SPM) 4.2
Swift Package Manager PackageDescription4.2introduces support of local dependencies.
Swift Package Manager PackageDescription4.2引入了对本地依赖项的支持。
Local dependencies are packages on disk that can be referred directly using their paths. Local dependencies are only allowed in the root package and they override all dependencies with same name in the package graph.
本地依赖项是磁盘上的包,可以使用它们的路径直接引用。本地依赖只允许在根包中,它们会覆盖包图中所有同名的依赖。
Note: I expect, but have not yet tested, that something like the following should be possible with SPM 4.2:
注意:我希望,但尚未测试,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"]
),
]
)
回答by MarkHim
With swift Swift 3 the syntax self.dynamicType
has been deprecated, use this instead
Swift Swift 3 的语法self.dynamicType
已被弃用,请改用它
let testBundle = Bundle(for: type(of: self))
let fooTxtPath = testBundle.path(forResource: "foo", ofType: "txt")
or
或者
let fooTxtURL = testBundle.url(forResource: "foo", withExtension: "txt")
回答by Sultan Ali
if you have multiple target in your project then you need to add resources between different target available in the Target Membershipand you may need to switch between different Targetas 3 steps shown in the figure below
如果您的项目中有多个目标,那么您需要在Target Membership 中可用的不同目标之间添加资源,您可能需要在不同的Target之间切换,如下图所示的 3 个步骤