Ich versuche, eine iOS-App mit TDD und dem neuen XCTest-Framework zu schreiben. Eine meiner Methoden ruft eine Datei aus dem Internet ab (mit einem NSURL-Objekt) und speichert sie in den Dokumenten des Benutzers. Die Signatur der Methode ähnelt:
- (void) fetchAndStoreImage:(NSURL *)imageUrl
Ich versuche, den Test für diese Methode so zu schreiben, dass er nicht fehlschlägt, wenn keine Verbindung zum Internet besteht. Mein Ansatz (aus einer vorherigen Frage entnommen ) besteht darin, die Methode mithilfe einer NSURL für ein Bild im lokalen Dateisystem aufzurufen.
Wenn ein neues Projekt mit aktivierten Komponententests erstellt wird, verfügt das Verzeichnis "Tests" über ein Unterverzeichnis mit dem Namen "Unterstützende Dateien". Ich nehme an, dort sollten meine Testbilder hingehen. Meine Frage ist, wie ich ein NSURL-Objekt erhalten kann, das auf ein Bild in diesem Verzeichnis verweist, da ich nicht möchte, dass Testbilder mit der Anwendung gebündelt werden. Jede Hilfe wird geschätzt.
Antworten:
Tatsächlich ist der
[NSBundle mainBundle]
beim Ausführen eines UnitTest nicht der Pfad Ihrer App, sondern / Developer / usr / bin, sodass dies nicht funktioniert.Der Weg, um Ressourcen in einem Unit-Test zu erhalten, ist hier: OCUnit & NSBundle
Kurz gesagt, verwenden Sie:
oder in deinem Fall:
quelle
NSBundle(forClass: self.dynamicType)
Swift 5.3
Hinweis: Swift 5.3 enthält Funktionen für Paketmanagerressourcen SE-0271 , die mit Ressourcen für Anwendungspakete und Testpakete verwendet werden können.
Swift 4, 5:
let testBundle = Bundle(for: type(of: self)) guard let fileURL = testBundle.url(forResource: "imageName", withExtension: "png") else { fatalError() } // path approach guard let filePath = bundle.path(forResource: "dataName", ofType: "csv") else { fatalError() } let fileUrl = URL(fileURLWithPath: filePath)
Das Bundle bietet Möglichkeiten, die Haupt- und Testpfade für Ihre Konfiguration zu ermitteln:
@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
Die Xcode-URL befindet sich in
Developer/Xcode/DerivedData
etwa ...file:///Users/ UserName/ Library/ Developer/ Xcode/ DerivedData/ App-qwertyuiop.../ Build/ Products/ Debug-iphonesimulator/ AppTests.xctest/ imageName.png
... die von der
Developer/CoreSimulator/Devices
URL getrennt istfile:///Users/ UserName/ Library/ Developer/ CoreSimulator/ Devices/ _UUID_/ data/ Containers/ Bundle/ Application/ _UUID_/ App.app/
Beachten Sie auch, dass die ausführbare Unit-Test-Datei standardmäßig mit dem Anwendungscode verknüpft ist. Der Komponententestcode sollte jedoch nur im Testpaket eine Zielmitgliedschaft enthalten. Der Anwendungscode sollte nur eine Zielmitgliedschaft im Anwendungspaket enthalten. Zur Laufzeit wird das Unit-Test- Zielpaket zur Ausführung in das Anwendungspaket eingefügt .
Swift Package Manager (SPM) 4:
let testBundle = Bundle(for: type(of: self)) print("testBundle.bundlePath = \(testBundle.bundlePath) ")
Hinweis: Standardmäßig erstellt die Befehlszeile
swift test
einMyProjectPackageTests.xctest
Testpaket. Und dasswift package generate-xcodeproj
wird einMyProjectTests.xctest
Testpaket erstellen . Diese verschiedenen Testpakete haben unterschiedliche Pfade . Außerdem können die verschiedenen Testpakete einige interne Verzeichnisstrukturen und Inhaltsunterschiede aufweisen .In beiden Fällen gibt das
.bundlePath
und.bundleURL
den Pfad des Testbündels zurück, das derzeit unter macOS ausgeführt wird. IstBundle
derzeit jedoch nicht für Ubuntu implementiert.Befehlszeile
swift build
undswift test
bieten derzeit keinen Mechanismus zum Kopieren von Ressourcen.Mit einigem Aufwand ist es jedoch möglich, Prozesse für die Verwendung des Swift Package Managers mit Ressourcen in den Befehlszeilenumgebungen macOS Xcode, macOS command line und Ubuntu einzurichten. Ein Beispiel finden Sie hier: 004.4'2 SW Dev Swift Package Manager (SPM) mit Ressourcen Qref
quelle
withExtension
. Danke.withType
wäre für das Abrufen eines PfadespathForResource("imageName", ofType: "png")
anstelle einer URL.self
verweist auf den beiliegenden Testclass SomeCaseTests: XCTestCase {..}
. Und.dynamicType
wertet den Wert des Laufzeittyps der Klasse aus. Siehe Apple Dev-Dokument und scrollen Sie zum Abschnitt Dynamic Type Expression .self.dynamicType
Andernfalls wird beim Versuch, das Bundle abzurufen , eine Fehlermeldung angezeigt . Bist du sicher, dass du nur füttern kannstNSBundle(forClass:)
self
?Nur um die richtige Antwort anzuhängen, ist dies ein Beispiel, wie Sie filePath für eine Datei in Ihren UnitTests / Supporting Files erhalten:
NSString *filePath = [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:@"YourFileName.json"]; XCTAssertNotNil(filePath);
Dies wird wahrscheinlich für jemanden nützlich sein.
quelle
So greifen Sie auf die lokale Datei zu:
NSURL*imageUrl=[[NSBundle mainBundle]URLForResource:@"imageName" withExtension:@"png"];
Sie können asynchron zugreifen und auf die Antwort warten, indem Sie Folgendes verwenden: https://github.com/travisjeffery/TRVSMonitor
Wenn Sie hinzugefügt haben:
dataset1.json
im Testziel (2):NSString *p=[[NSBundle mainBundle] pathForResource:@"dataset1" ofType:@"json"]; NSLog(@"%@",p);
quelle
Swift
Version basierend auf akzeptierter Antwort:let url = URL(fileURLWithPath: Bundle(for: type(of: self)).path(forResource: "my", ofType: "json") ?? "TODO: Proper checking for file")
quelle
Das Problem, mit dem ich konfrontiert war, war, dass der Anwendungscode versuchte, auf das Hauptpaket für Dinge wie bundleIdentifier zuzugreifen, und da das Hauptpaket nicht mein Unit-Test-Projekt war, gab es null zurück.
Ein Hack, der sowohl in Swift 3.0.1 als auch in Objective-C funktioniert, besteht darin, eine Objective-C-Kategorie in NSBundle zu erstellen und in Ihr Unit-Test-Projekt aufzunehmen. Sie benötigen keinen Bridging-Header oder ähnliches. Diese Kategorie wird geladen. Wenn Sie jetzt im Anwendungscode nach dem Hauptpaket fragen, gibt Ihre Kategorie das Unit-Test-Paket zurück.
@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
quelle
Hier ist die Swift-Version von Xcode 7, iOS 9 usw.
let testBundle = NSBundle(forClass: self.dynamicType) let path = testBundle.pathForResource("someImage", ofType: "jpg") XCTAssertNotNil(path)
Hinweis: someImage.jpg muss in Ihrem Testziel enthalten sein.
Edit: Swift 5
let testBundle = Bundle(for: type(of: self)) let path = testBundle.path(forResource: "someImage", ofType: "jpg") XCTAssertNotNil(path)
quelle
Swift 5
let testBundle = Bundle(for: self)
Die Verwendung
let testBundle = Bundle(for: type(of: self))
, die in einigen der obigen Antworten enthalten ist, würde nicht kompiliert und stattdessen konsistent einen Fehler des Segmentierungsfehlers erzeugt: 11 für mich.quelle