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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 03:13:33  来源:igfitidea点击:

Why can't code inside unit tests find bundle resources?

cocoaunit-testingxcodensbundleoctest

提问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.txtis 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/DerivedDatasomething 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/Devicesregular (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 testwill create a MyProjectPackageTests.xctesttest bundle. And, the swift package generate-xcodeprojwill create a MyProjectTests.xctesttest 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 .bundlePathand .bundleURLwill return the path of test bundle currently being run on macOS. However, Bundleis not currently implemented for Ubuntu Linux.

在任何一种情况下,.bundlePath.bundleURL都将返回当前在 macOS 上运行的测试包的路径。但是,Bundle目前尚未为 Ubuntu Linux 实现。

Also, command line swift buildand swift testdo not currently provide a mechanism for copying resources.

此外,命令行swift buildswift 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.dynamicTypehas 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 mishimay

Confirm that the resource is added to the test target.

确认资源已添加到测试目标。

enter image description here

在此处输入图片说明

回答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 个步骤

enter image description here

在此处输入图片说明

回答by drew..

I had to ensure this General Testing checkbox was set this General Testing checkbox was set

我必须确保设置了这个 General Testing 复选框 此常规测试复选框已设置