首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么单元测试中的代码不能找到包资源?

为什么单元测试中的代码不能找到包资源?
EN

Stack Overflow用户
提问于 2009-12-10 15:37:32
回答 6查看 54.7K关注 0票数 197

我正在进行单元测试的一些代码需要加载资源文件。它包含以下行:

代码语言:javascript
复制
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];

在应用程序中,它运行得很好,但当由单元测试框架运行时,pathForResource:返回nil,这意味着它无法定位foo.txt

我已经确保foo.txt包含在单元测试目标的Copy Bundle Resources构建阶段,那么为什么它找不到这个文件呢?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2009-12-10 15:40:26

当单元测试工具运行您的代码时,您的单元测试包是而不是主包。

即使您正在运行测试,而不是您的应用程序,您的应用程序包仍然是主包。(这可能会防止您正在测试的代码搜索错误的包。)因此,如果您将资源文件添加到单元测试包中,则在搜索主包时将找不到它。如果将上一行替换为:

代码语言:javascript
复制
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"foo" ofType:@"txt"];

然后,您的代码将搜索您的单元测试类所在的包,一切都会好起来的。

票数 332
EN

Stack Overflow用户

发布于 2015-06-07 05:50:23

一个Swift实现:

Swift 2

代码语言:javascript
复制
let testBundle = NSBundle(forClass: self.dynamicType)
let fileURL = testBundle.URLForResource("imageName", withExtension: "png")
XCTAssertNotNil(fileURL)

Swift 3、Swift 4

代码语言:javascript
复制
let testBundle = Bundle(for: type(of: self))
let filePath = testBundle.path(forResource: "imageName", ofType: "png")
XCTAssertNotNil(filePath)

捆绑包提供了发现配置的主路径和测试路径的方法:

代码语言:javascript
复制
@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中,类似于...

代码语言:javascript
复制
/Users/
  UserName/
    Library/
      Developer/
        Xcode/
          DerivedData/
            App-qwertyuiop.../
              Build/
                Products/
                  Debug-iphonesimulator/
                    AppTests.xctest/
                      foo.txt

..。它与Developer/CoreSimulator/Devices 常规(非单元测试)包路径是分开的

代码语言:javascript
复制
/Users/
  UserName/
    Library/
    Developer/
      CoreSimulator/
        Devices/
          _UUID_/
            data/
              Containers/
                Bundle/
                  Application/
                    _UUID_/
                      App.app/

另请注意,默认情况下,单元测试可执行文件与应用程序代码链接。但是,单元测试代码应该只在测试包中具有Target成员资格。应用程序代码在应用程序捆绑包中应该只有Target成员资格。在运行时,单元测试目标包是。

Swift包管理器(SPM) 4:

代码语言:javascript
复制
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 buildswift 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)__“的演进提案。:-)

资源并不总是供包的客户端使用;资源的一次使用可能包括仅由单元测试所需的测试装置。这样的资源不会与库代码一起并入包的客户端,而是仅在运行包的测试时使用。

  • targettestTarget 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,选项:选项)

示例

代码语言:javascript
复制
// 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编译器指令以使用资源。

代码语言:javascript
复制
#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
票数 103
EN

Stack Overflow用户

发布于 2016-09-21 16:40:53

在swift 3中,语法self.dynamicType已被弃用,请使用以下语法

代码语言:javascript
复制
let testBundle = Bundle(for: type(of: self))
let fooTxtPath = testBundle.path(forResource: "foo", ofType: "txt")

代码语言:javascript
复制
let fooTxtURL = testBundle.url(forResource: "foo", withExtension: "txt")
票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1879247

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档