web-dev-qa-db-fra.com

Pourquoi le code dans les tests unitaires ne trouve-t-il pas les ressources d'ensemble?

Certains codes que je suis en train de tester doivent charger un fichier de ressources. Il contient la ligne suivante:

NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];

Dans l'application, il fonctionne très bien, mais lorsqu'il est exécuté par la structure de test unitaire, pathForResource: renvoie nil, ce qui signifie qu'il n'a pas pu localiser foo.txt.

Je me suis assuré que foo.txt est inclus dans la phase Copier les ressources du paquet build de la cible de test unitaire, alors pourquoi ne peut-il pas trouver le fichier?

160
benzado

Lorsque le harnais de test unitaire exécute votre code, votre lot de test unitaire estPASle lot principal.

Même si vous exécutez des tests, et non votre application, votre bundle d'applications reste le bundle principal. (Cela empêchera vraisemblablement le code que vous testez de rechercher le mauvais ensemble.) Ainsi, si vous ajoutez un fichier de ressources au groupe de tests unitaires, vous ne le trouverez pas si vous recherchez le groupe principal. Si vous remplacez la ligne ci-dessus par:

NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"foo" ofType:@"txt"];

Ensuite, votre code recherchera le bundle dans lequel se trouve votre classe de test unitaire et tout ira bien.

293
benzado

Une implémentation rapide:

Swift 2

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

Swift 3, Swift 4

let testBundle = Bundle(for: type(of: self))
let filePath = testBundle.path(forResource: "imageName", ofType: "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

Dans Xcode 6 | 7 | 8 | 9, un chemin d'accès au bundle unit-test sera dans Developer/Xcode/DerivedData quelque chose comme ... 

/Users/
  UserName/
    Library/
      Developer/
        Xcode/
          DerivedData/
            App-qwertyuiop.../
              Build/
                Products/
                  Debug-iphonesimulator/
                    AppTests.xctest/
                      foo.txt

... qui est distinct du chemin Developer/CoreSimulator/Devices _ regular (non-unit-test):

/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 Linux.

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

Voir aussi: Utiliser des ressources dans les tests unitaires avec Swift Package Manager

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"]
        ),
    ]
)
59
l --marc l

Avec Swift Swift 3, la syntaxe self.dynamicType est obsolète, utilisez plutôt ceci

let testBundle = Bundle(for: type(of: self))
let fooTxtPath = testBundle.path(forResource: "foo", ofType: "txt")

ou 

let fooTxtURL = testBundle.url(forResource: "foo", withExtension: "txt")
12
MarkHim

Confirmez que la ressource est ajoutée à la cible de test.

 enter image description here

4
mishimay

Je devais m'assurer que cette case General Testing était cochée  this General Testing checkbox was set

0
drew..

si vous avez plusieurs cibles dans votre projet, vous devez ajouter des ressources entre différentes cibles disponibles dans Adhésion à la cible et vous devrez peut-être basculer entre différentes Cible en 3 étapes, comme indiqué dans la figure ci-dessous. 

 enter image description here

0
Sultan Ali