web-dev-qa-db-fra.com

NSURL vers le chemin du fichier dans le groupe de tests avec XCTest

J'essaie d'écrire une application iOS à l'aide de TDD et du nouveau framework XCTest. Une de mes méthodes récupère un fichier sur Internet (à partir d'un objet NSURL) et le stocke dans les documents de l'utilisateur. La signature de la méthode est similaire à:

- (void) fetchAndStoreImage:(NSURL *)imageUrl

J'essaie d'écrire le test pour cette méthode de manière à ne pas échouer s'il n'y a pas de connexion à Internet. Mon approche (tirée d'une précédente question ) consiste à appeler la méthode à l'aide d'un NSURL vers une image du système de fichiers local.

Lorsqu'un nouveau projet avec les tests unitaires activés est créé, le répertoire Tests comporte un sous-répertoire nommé "Fichiers de support". Je suppose que c'est là que mes images de test devraient aller. Ma question est la suivante: comment obtenir un objet NSURL qui pointe vers une image de ce répertoire, car je ne voudrais pas que les images de test soient associées à l'application. Toute aide est appréciée.

66

En fait, le [NSBundle mainBundle] lors de l'exécution d'un UnitTest est not le chemin d'accès de votre application, mais est/Developer/usr/bin.

Voici comment trouver des ressources dans un test unitaire: OCUnit & NSBundle

En bref, utilisez:

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

ou dans votre cas:

[[NSBundle bundleForClass:[self class]] resourceURL]
117
Ben Clayton

Swift 2:

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

Swift 3, 4: 

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

Bundle fournit des moyens de découvrir les chemins principal et de test de votre configuration:

@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

L'URL Xcode 6 | 7 | 8 sera dans Developer/Xcode/DerivedData quelque chose comme ... 

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

... qui est séparé de l'URL Developer/CoreSimulator/Devices 

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

Notez également que l'exécutable de test unitaire est, par défaut, lié au code de l'application. Toutefois, le code de test d'unité ne doit avoir que l'appartenance à la cible dans le lot de test. Le code de l'application doit uniquement avoir l'appartenance à la cible dans le bundle d'application. Au moment de l'exécution, le groupe de cibles de test unitaire est injecté dans le groupe d'applications pour être exécuté .

Gestionnaire de paquets Swift (SPM) 4:

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

Remarque: Par défaut, la ligne de commande Swift test crée un ensemble de tests MyProjectPackageTests.xctest. Et, le Swift package generate-xcodeproj créera un ensemble de tests MyProjectTests.xctest. Ces différents ensembles de tests ont différents chemins. De plus, les différents ensembles de tests peuvent avoir une structure de répertoire internal et des différences de contenu. 

Dans les deux cas, .bundlePath et .bundleURL renverront le chemin du bundle de test en cours d'exécution sur macOS. Cependant, Bundle n'est actuellement pas implémenté pour Ubuntu.

De plus, les lignes de commande Swift build et Swift test ne fournissent pas actuellement de mécanisme de copie des ressources. 

Cependant, avec quelques efforts, il est possible de configurer des processus pour utiliser Swift Package Manager avec des ressources des environnements macOS Xcode, macOS en ligne de commande et Ubuntu. Vous trouverez un exemple ici: 004.4'2 SW Dev Gestionnaire de packages (SPM) avec ressources Qref

Gestionnaire de paquets Swift (SPM) 4.2

Swift Package Manager PackageDescription 4.2 introduit le support des dépendances locales

Les dépendances locales sont des packages sur disque qui peuvent être référencés directement à l'aide de leurs chemins. Les dépendances locales ne sont autorisées que dans le package racine et remplacent toutes les dépendances du même nom dans le graphique du package.

Remarque: j'espère, mais je n'ai pas encore testé, que SPM 4.2 devrait permettre:

// Swift-tools-version:4.2
import PackageDescription

let package = Package(
    name: "MyPackageTestResources",
    dependencies: [
        .package(path: "../test-resources"),
    ],
    targets: [
        // ...
        .testTarget(
            name: "MyPackageTests",
            dependencies: ["MyPackage", "MyPackageTestResources"]
        ),
    ]
)
37
l --marc l

Juste pour annexer la bonne réponse, voici un exemple de méthode pour obtenir filePath pour un fichier dans vos fichiers UnitTests/Supporting: 

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

Cela sera probablement utile pour quelqu'un.

13
wzbozon
  1. Vous pouvez référencer des fichiers de lots comme dans n'importe quelle cible.
  2. Vérifiez si le fichier est copié lors de la phase de construction de la copie des ressources du bundle (de votre cible de test)
  3. Pour accéder au fichier local:

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

Vous pouvez effectuer un accès asynchrone et attendre la réponse en utilisant: https://github.com/travisjeffery/TRVSMonitor

Si vous avez ajouté: dataset1.json dans la cible de test (2): 

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

2013-10-29 15: 49: 30.547 PlayerSample [13771: 70b] WT (0):/Utilisateurs/bpds/Bibliothèque/Support d'application/iPhone Simulator/7.0/Applications/7F78780B-684A-40E0-AA35-A3B5D8AA9DBD/PlayerSample. app.app/dataset1.json 

3
bpds

Swift version basée sur la réponse acceptée:

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

Le problème que je rencontrais était que le code de l'application essayait d'accéder au paquet principal pour des choses comme bundleIdentifier et que le paquet principal n'était pas mon projet de test unitaire, il renverrait zéro.

Un hack autour de cela qui fonctionne à la fois dans Swift 3.0.1 et Objective-C consiste à créer une catégorie Objective-C sur NSBundle et à l’inclure dans votre projet de test unitaire. Vous n'avez pas besoin d'un en-tête de pontage ou quoi que ce soit. Cette catégorie sera chargée et maintenant, lorsque votre code d'application vous demandera le lot principal, votre catégorie renverra le lot de tests unitaires.

@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
1
odyth

Voici la version Swift de cela, Xcode 7, iOS 9, etc.

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

Remarque: someImage.jpg doit être inclus dans votre cible de test.

0
Dan Rosenstark