使用 XCTest 将 NSURL 转换为测试包中的文件路径

我正在尝试使用 TDD 和新的 XCTest 框架编写一个 iOS 应用程序。我的一个方法从因特网检索一个文件(给定一个 NSURL 对象)并将其存储在用户的文档中。该方法的签名类似于:

- (void) fetchAndStoreImage:(NSURL *)imageUrl

我正在尝试为这个方法编写一个测试,它不会失败,如果没有连接到互联网。我的方法(取自以前的 有个问题)是使用 NSURL 对本地文件系统中的图像调用该方法。

当创建一个启用了单元测试的新项目时,测试目录有一个名为“支持文件”的子目录。我想这就是我的测试图像应该去的地方。我的问题是如何获得指向此目录中图像的 NSURL 对象,因为我不希望将测试图像与应用程序绑定在一起。感谢你的帮助。

43427 次浏览
  1. 您可以像在任何目标中那样引用 bundle 文件。
  2. 检查该文件是否在 CopyBundle 资源构建阶段(测试目标)进行了拷贝
  3. 访问本地文件:

    NSURL*imageUrl=[[NSBundle mainBundle]URLForResource:@"imageName" withExtension:@"png"];
    

You can make asynchronous access and wait for the response using : https://github.com/travisjeffery/TRVSMonitor

If you have added : dataset1.json in the test target (2) :

NSString *p=[[NSBundle mainBundle] pathForResource:@"dataset1" ofType:@"json"];
NSLog(@"%@",p);

2013-10-2915:49:30.547 PlayerSample [13771:70b ] WT (0) :/Users/bpds/Library/Application Support/iPhone Simulator/7.0/Applications/7F78780B-684A-40E0-AA35-A3B5D8AA9DBD/PlayerSample.app.app/datet1.json

事实上,运行 UnitTest 时的 [NSBundle mainBundle]是应用程序的路径 没有,但是是/Developer/usr/bin,所以这不会起作用。

在单元测试中获取资源的方法如下: OCUnit & NSBundle

简而言之,使用:

[[NSBundle bundleForClass:[self class]] resourcePath]

或者对你来说:

[[NSBundle bundleForClass:[self class]] resourceURL]

为了补充正确答案,下面是如何在 UnitTest/Support Files 中获取文件的 filePath 示例:

NSString *filePath = [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:@"YourFileName.json"];
XCTAssertNotNil(filePath);

这可能对某些人有用。

斯威夫特5.3

注意: Swift 5.3包含 包管理器资源 SE-0271功能,可以与捆绑和测试包资源一起使用。

资源并不总是供包的客户机使用; 资源的一种用途可能包括单元测试所需的测试装置。这些资源不会连同库代码一起合并到包的客户机中,而只会在运行包的测试时使用。

雨燕4,5:

let testBundle = Bundle(for: type(of: self))
guard var fileUrl = testBundle.url(forResource: "imageName", withExtension: "png")
else { fatalError() }


// path approach
guard let filePath = testBundle.path(forResource: "dataName", ofType: "csv")
else { fatalError() }
let fileUrl = URL(fileURLWithPath: 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 URL 将在 Developer/Xcode/DerivedData中类似于..。

file:///Users/
UserName/
Library/
Developer/
Xcode/
DerivedData/
App-qwertyuiop.../
Build/
Products/
Debug-iphonesimulator/
AppTests.xctest/
imageName.png

... 这是独立于 Developer/CoreSimulator/Devices网址

file:///Users/
UserName/
Library/
Developer/
CoreSimulator/
Devices/
_UUID_/
data/
Containers/
Bundle/
Application/
_UUID_/
App.app/

另请注意,默认情况下,单元测试可执行文件与应用程序代码链接。但是,单元测试代码应该只在测试包中包含 TargetMembership。应用程序代码应该只包含目标成员捆绑。在运行时,单元测试目标包是 注射到捆绑里执行死刑

快速软件包管理器(SPM)4:

let testBundle = Bundle(for: type(of: self))
print("testBundle.bundlePath = \(testBundle.bundlePath) ")

注意: 默认情况下,命令行 swift test将创建一个 MyProjectPackageTests.xctest测试包。而且,swift package generate-xcodeproj将创建一个 MyProjectTests.xctest测试包。这些不同的测试包有 不同的道路此外,不同的测试包可能有一些 < strong > 内部目录结构和内容差异 。

在这两种情况下,.bundlePath.bundleURL都将返回当前在 macOS 上运行的测试包的路径。但是,目前还没有为 Ubuntu 的 Swift 4实现 Bundle

此外,Swift 4命令行 swift buildswift test不提供复制资源的机制。

但是,通过一些努力,可以设置使用 Swift Package Manger 的进程,其中包含 macOS Xcode、 macOS 命令行和 Ubuntu 命令行环境中的资源。这里可以找到一个例子: 004.4’2 SW Dev Swift Package Manager (SPM) With Resources Qref

这是 Swift 的版本,Xcode 7,iOS 9等等。

let testBundle = NSBundle(forClass: self.dynamicType)
let path = testBundle.pathForResource("someImage", ofType: "jpg")
XCTAssertNotNil(path)

注意: 您的测试目标中必须包含 some Image.jpg。


编辑: Swift 5

let testBundle = Bundle(for: type(of: self))
let path = testBundle.path(forResource: "someImage", ofType: "jpg")
XCTAssertNotNil(path)

我所面临的问题是,应用程序代码试图访问 bundleIdentifier 之类的主绑定包,由于主绑定包不是我的单元测试项目,所以它将返回 nil。

一个在 Swift 3.0.1和 Objective-C 中都适用的技巧是在 NSBundle 上创建 Objective-C 类别,并将其包含在您的单元测试项目中。您不需要桥接头或任何东西。此类别将被加载,现在当应用程序代码请求主包时,您的类别将返回单元测试包。

@interface NSBundle (MainBundle)


+(NSBundle *)mainBundle;


@end


@implementation NSBundle (Main)


#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+(NSBundle *)mainBundle
{
return [NSBundle bundleForClass:[SomeUnitTest class]];
}
#pragma clang diagnostic pop


@end

基于公认答案的 Swift版本:

let url = URL(fileURLWithPath: Bundle(for: type(of: self)).path(forResource: "my", ofType: "json") ?? "TODO: Proper checking for file")

Swift 5

let testBundle = Bundle(for: self)

使用 let testBundle = Bundle(for: type(of: self))(在上面的一些答案中可以找到)不会编译,而是一直为我生成一个 内存区段错误: 11错误。