it-swarm.com.ru

Почему код внутри модульных тестов не может найти ресурсы пакета?

Некоторый код, который я тестирую, должен загрузить файл ресурсов. Он содержит следующую строку:

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

В приложении все работает нормально, но при запуске средой модульного тестирования pathForResource: возвращает nil, что означает, что он не может найти foo.txt.

Я убедился, что foo.txt включен в фазу Copy Bundle Resources build цели модульного теста, так почему же он не может найти файл?

160
benzado

Когда ваш модуль запускает модульное тестирование, ваш пакет модульного тестирования являетсяНЕосновным пакетом.

Даже если вы запускаете тесты, а не ваше приложение, ваш пакет приложений по-прежнему остается основным. (Предположительно, это препятствует тому, чтобы код, который вы тестируете, искал неправильный пакет.) Таким образом, если вы добавите файл ресурсов в пакет модульного тестирования, вы не найдете его, если будете искать в основном пакете. Если вы замените вышеуказанную строку на:

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

Затем ваш код будет искать пакет, в котором находится ваш класс модульного тестирования, и все будет хорошо.

293
benzado

Реализация Swift:

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 предоставляет способы обнаружения основных и тестовых путей для вашей конфигурации:

@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 | 9 путь к пакету unit-test будет в Developer/Xcode/DerivedData что-то вроде ... 

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

... который отделен от пути к пакету Developer/CoreSimulator/Devicesnormal (non-unit-test):

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

Также обратите внимание, что исполняемый файл модульного теста по умолчанию связан с кодом приложения. Однако код модульного теста должен иметь только целевое членство только в комплекте тестов. Код приложения должен иметь только целевое членство в комплекте приложений. Во время выполнения целевым комплектом модульного теста является внедренный в комплект приложения для выполнения . 

Swift Package Manager (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 Linux.

Кроме того, командная строка Swift build и Swift test в настоящее время не предоставляют механизм для копирования ресурсов. 

Однако, приложив некоторые усилия, можно настроить процессы для использования Swift Package Manger с ресурсами в средах macOS Xcode, macOS и Ubuntu. Один пример можно найти здесь: 004.4'2 SW Dev Swift Package Manager (SPM) с ресурсами Qref

Смотрите также: Использование ресурсов в модульных тестах с Swift Package Manager

Swift Package Manager (SPM) 4.2

Swift Package Manager PackageDescription 4.2 представляет поддержку локальные зависимости

Локальные зависимости - это пакеты на диске, на которые можно напрямую ссылаться, используя их пути. Локальные зависимости разрешены только в корневом пакете, и они переопределяют все зависимости с тем же именем в графе пакета.

Примечание: я ожидаю, но еще не проверял, что что-то вроде следующего должно быть возможно с SPM 4.2:

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

С Swift Swift 3 синтаксис self.dynamicType устарел, используйте его вместо

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

или же 

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

Убедитесь, что ресурс добавлен к цели тестирования.

 enter image description here

4
mishimay

Я должен был убедиться, что этот флажок General Testing был установлен  this General Testing checkbox was set

0
drew..

если у вас есть несколько целей в вашем проекте, вам нужно добавить ресурсы между различными целями, доступными в Target Membership, и вам может потребоваться переключиться между различными Target, как 3 шага, показанные на рисунке ниже 

 enter image description here

0
Sultan Ali