NSURL до шляху до файлу в тестовому наборі з XCTest


77

Я намагаюся написати додаток для iOS, використовуючи TDD та новий фреймворк XCTest. Один із моїх методів отримує файл з Інтернету (із заданим об’єктом NSURL) і зберігає його в документах користувача. Підпис методу подібний до:

- (void) fetchAndStoreImage:(NSURL *)imageUrl

Я намагаюся написати тест для цього методу таким чином, щоб він не провалився, якщо немає підключення до Інтернету. Мій підхід (взятий з попереднього запитання ) полягає у виклику методу за допомогою NSURL до зображення в локальній файловій системі.

Коли створюється новий проект із увімкненими модульними тестами, каталог Тести має підкаталог із назвою «Підтримуючі файли». Думаю, саме сюди повинні йти мої тестові зображення. Моє питання полягає в тому, як я можу отримати об’єкт NSURL, який вказує на зображення в цьому каталозі, оскільки я б не хотів, щоб тестові зображення були в комплекті з додатком. Будь-яка допомога вдячна.


Ця відповідь вирішує цю проблему, не змінюючи код stackoverflow.com/a/24330617/468868
Карлос Рікардо,

Відповіді:


131

Насправді [NSBundle mainBundle]при запуску UnitTest це не шлях до вашої програми, а це / Developer / usr / bin, тому це не спрацює.

Отримати ресурси в модульному тесті можна тут: OCUnit & NSBundle

Коротше, використовуйте:

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

або у вашому випадку:

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

@pshah Так, я так вважаю, хоча сам не пробував.
Бен Клейтон,

21
Це надзвичайно корисно. До речі, у Свіфті цеNSBundle(forClass: self.dynamicType)
Білл

51

Свіфт 5.3

Примітка: Swift 5.3 включає можливості Resource Manager Resources SE-0271, які можна використовувати з пакетами додатків та тестовими ресурсами.

Ресурси не завжди призначені для використання клієнтами пакету; одне використання ресурсів може включати тестові пристрої, які потрібні лише для модульних тестів. Такі ресурси не включатимуться до клієнтів пакету разом із бібліотечним кодом, а використовуватимуться лише під час запуску тестів пакета.

Свіфт 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)

Набір надає способи виявити основні та тестові шляхи для вашої конфігурації:

@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

URL-адреса Xcode буде виглядати Developer/Xcode/DerivedDataприблизно так ...

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

... що окремо від Developer/CoreSimulator/DevicesURL-адреси

file:///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.

Крім того, командний рядок swift buildіswift test даний час не забезпечує механізм копіювання ресурсів.

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


Дякую! Спробуючи ваш код, це повинен бути URLForResource ("imageName", withExtension: "png")
Кіріон,

@Ciryon Приклад тепер виправлено withExtension. Дякую. withTypeбуде для отримання шляху pathForResource("imageName", ofType: "png")замість URL-адреси.
l --marc l

що таке self.dynamicType?
Раміс

@Ramis selfпосилається на тест, що охоплює class SomeCaseTests: XCTestCase {..}. І, .dynamicTypeоцінює значення типу виконання класу. див. документ Apple Dev і перейдіть до розділу Вираз динамічного типу .
l --marc l

@ l - marcl Я використовую Xcode 7, але все одно повинен використовувати, self.dynamicTypeінакше з’являється помилка при спробі захопити комплект. Ви впевнені, що можете просто нагодувати NSBundle(forClass:) self?
Ziewvater

14

Щоб додати правильну відповідь, ось приклад того, як отримати filePath для файлу у ваших UnitTests / допоміжних файлах:

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

Це, мабуть, буде корисно комусь.


3
  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


Я намагався, і, здається, це не працює. Очевидно, зображення в тестовому наборі не копіюються в основний пакет. Шлях - відповідь Бена, ви повинні отримати тестовий комплект, використовуючи [NSBundle bundleForClass: [self class]] з класу тестового випадку.
Едуардо Аренас Прада

1
Цікаво, що це, здається, працює (принаймні для цілей симулятора ios.) Вам обов’язково потрібно переконатися, що ви додаєте ресурс до фази збірки ресурсів набору тестових цілей.
voidref

2

Swift версія на основі прийнятої відповіді:

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

1

Проблема, з якою я стикався, полягала в тому, що код програми намагався отримати доступ до основного пакета для таких речей, як bundleIdentifier, і оскільки основний пакет не був моїм тестовим проектом, він повернув би нуль.

Хакерство навколо цього, що працює як в Swift 3.0.1, так і в Objective-C, полягає у створенні категорії Objective-C на NSBundle та включенні її у ваш тестовий проект. Вам не потрібен мостовий заголовок або щось інше. Ця категорія завантажиться, і тепер, коли код програми запитує основний пакет, ваша категорія поверне модульний тестовий пакет.

@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

0

Ось версія Swift цього, Xcode 7, iOS 9 тощо.

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

Примітка: someImage.jpg повинен бути включений до вашої тестової цілі.


Редагувати: Свіфт 5

let testBundle = Bundle(for: type(of: self))
let path = testBundle.path(forResource: "someImage", ofType: "jpg")
XCTAssertNotNil(path)

0

Стрімкий 5

let testBundle = Bundle(for: self)

Використання let testBundle = Bundle(for: type(of: self)), яке міститься в деяких відповідях вище, не компілюється, а натомість постійно видає помилку помилки сегментації: 11 для мене.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.