2013-10-11 103 views
60

我正在嘗試使用TDD和新的XCTest框架編寫iOS應用程序。我的一個方法從互聯網上檢索一個文件(給定一個NSURL對象)並將其存儲在用戶的文檔中。該方法的簽名是類似於:NSURL到XCTest測試包中的文件路徑

- (void) fetchAndStoreImage:(NSURL *)imageUrl 

我想寫這個方法的測試的方式,如果有互聯網連接沒有不失敗的。我的方法(取自以前的question)是使用NSURL調用本地文件系統中的映像的方法。

當創建一個啓用了單元測試的新項目時,Tests目錄有一個名爲'Supporting Files'的子目錄。我想這是我的測試圖像應該去的地方。我的問題是,如何獲得指向此目錄中圖像的NSURL對象,因爲我不希望測試圖像與應用程序捆綁在一起。任何幫助表示讚賞。

+0

這個回答解決了問題,而無需更改代碼http://stackoverflow.com/a/24330617/468868 –

回答

108

事實上,運行UnitTest時的[NSBundle mainBundle]是而不是您的應用程序的路徑,但是是/ Developer/usr/bin,所以這不起作用。

獲得資源的單元測試的方式是在這裏:OCUnit & NSBundle

總之,用途:

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

或在您的情況:

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

這也適用於應用程序目標? – pshah

+0

@pshah是的,我相信如此,儘管我自己並沒有嘗試過。 –

+13

這非常有幫助。順便說一句,在Swift中,它是'NSBundle(forClass:self.dynamicType)' – Bill

2
  1. 您可以像引用任何目標一樣引用包文件。
  2. 檢查文件是否在複製包資源拷入建立階段(測試目標)
  3. 要訪問本地文件:

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

您可以異步訪問並等待用於使用所述響應:https://github.com/travisjeffery/TRVSMonitor

如果已經添加了:在dataset1.json測試目標(2):

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

    2013-10-29 15: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/dataset1.json  
+0

我想,它似乎並沒有工作。顯然,測試包中的圖像不會被複制到主包中。在Ben的回答中,你必須使用測試用例類中的[NSBundle bundleForClass:[self class]]來獲得測試包。 –

+1

有趣的是,這看起來確實有效(至少對於iOS模擬器目標)。*絕對*需要確保將資源添加到測試目標的副本捆綁資源構建階段。 – voidref

13

只是追加更正回答,這是示例如何在UnitTests/Supporting Files中獲取filePath的文件路徑:

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

這可能對某人有用。

29

夫特2:

let testBundle = NSBundle(forClass: self.dynamicType) 
let fileURL = testBundle.URLForResource("imageName", withExtension: "png") 
XCTAssertNotNil(fileURL) 

夫特3,4:

let testBundle = Bundle(for: type(of: self)) 
let fileURL = testBundle.url(forResource: "imageName", withExtension: "png") 
XCTAssertNotNil(filePath) 

捆綁提供了一些方法來發現爲您的配置的主要和測試路徑:

@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網址將在Developer/Xcode/DerivedData像...

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

...這是獨立於Developer/CoreSimulator/Devices URL

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

還要注意單元測試可執行文件默認情況下,與應用程序代碼鏈接。但是,單元測試代碼只應在測試包中具有目標成員資格。應用程序代碼應該只在應用程序包中具有目標成員資格。在運行時,單元測試目標包是injected into the application bundle for execution

夫特包管理器(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上運行的測試包的路徑。但是,Bundle目前尚未針對Ubuntu實施。

此外,命令行swift buildswift test目前不提供複製資源的機制。

但是,通過一些努力,可以爲macOS Xcode,macOS命令行和Ubuntu命令行環境中的資源設置使用Swift軟件包管理器的進程。一個例子可以在這裏找到:004.4'2 SW Dev Swift Package Manager (SPM) With Resources Qref

+0

謝謝!嘗試你的代碼,它應該是URLForResource(「imageName」,withExtension:「PNG」) – Ciryon

+0

@Ciryon示例現在更正爲'withExtension'。謝謝。 'withType'將用於檢索路徑['pathForResource(「imageName」,ofType:「png」)'](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes /NSBundle_Class/index.html)而不是URL。 –

+0

什麼是self.dynamicType? – Ramis

0

下面是這個雨燕版本時,Xcode 7,iOS的9等

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

注:someImage.jpg必須包含在您的測試目標。

1

我面臨的問題是應用程序代碼試圖訪問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