Когда источники приложения должны быть включены в тестовые цели?

В новом проекте у меня есть этот простой тест

#import <XCTest/XCTest.h>
#import "ViewController.h"

@interface ViewControllerTests : XCTestCase
@end

@implementation ViewControllerTests

- (void)testExample
{ 
    // Using a class that is not in the test target.
    ViewController * viewController = [[ViewController alloc] init];
    XCTAssertNotNil(viewController, @"");
}

@end

ViewController.h - это не часть тестовой цели, но это компилирует и запускает тесты без проблем.

enter image description here

Я думаю, это связано с тем, что приложение сначала создается (как зависимость), чем тесты. Затем компоновщик показывает, что такое класс ViewController.

Однако в более старом проекте, с точно таким же тестом и файлом ViewController, сборка завершилась неудачей на фазе компоновщика:

Undefined symbols for architecture i386:
"_OBJC_CLASS_$_ViewController", referenced from:
  objc-class-ref in ViewControllerTests.o

Эта ошибка компоновщика возникает, даже если при создании нового целевого объекта тестирования XCTest.

Чтобы обойти это, можно включить источники как в приложение, так и в целевые объекты (отметьте оба поля на изображении выше). Это вызывает предупреждения сборки для повторяющихся символов в журнале системы симулятора (откройте симулятор и нажмите cmd-/, чтобы увидеть это):

Class ViewController is implemented in both 
[...]/iPhone Simulator/ [...] /MyApp.app/MyApp and 
[...]/Debug-iphonesimulator/LogicTests.octest/LogicTests. 
One of the two will be used. Which one is undefined.

Эти предупреждения иногда вызывают проблемы, проиллюстрированные в следующем примере:

 [viewController isKindOfClass:[ViewController class]]; // = NO
 // Memory address of the `Class` objects are different.

 NSString * instanceClassString = NSStringFromClass([viewController class]);
 NSString * classString         = NSStringFromClass([ViewController class]);

 [instanceClassString isEqualToString:classString]; // = YES
 // The actual class names are identical

Итак, вопрос в том, какие параметры в старшем проекте требуют включения исходных файлов приложения в тестовую цель?


Сводка комментариев

Между рабочим и нерабочим проектом:

  • В компоновщике нет разницы (команда, начинающаяся с Ld).
  • В целевых зависимостях нет разницы (существует 1 зависимость от тестовой цели, которая является приложением)
  • В настройках компоновщика нет никакой разницы.

Ответ 1

Я потратил некоторое время на это.

Если вы читаете эту документацию, вы обнаружите, что Xcode имеет два режима для запуска тестов. Логические тесты и тесты приложений. Разница заключается в том, что тесты Logic создают свою собственную цель с помощью ваших классов и символов, встроенных в нее. Полученный исполняемый файл может быть запущен в симуляторе и выводит результаты тестирования обратно в Xcode. С другой стороны, тесты приложений создают динамическую библиотеку, связанную с вашим кодом, который вводится в приложение во время выполнения. Это позволяет запускать тесты в среде iPhone и тестировать загрузку Xib и другие вещи.

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

Как и в наши дни Xcode кажется пытается не различать два и значения по умолчанию для создания целевого объекта Application Tests, позволяет пройти через все вещи, которые вам, возможно, придется изменить, чтобы превратить вашу тестовую задачу Logic в unit test.

Я также предполагаю, что у вас есть целевая программа, а не статичная библиотека, так как направления будут немного разными.

  • В настройках сборки для тестового целевого объекта удалите настройки сборки "Bundle Loader" и "Test Host". Мы получим Xcode, чтобы добавить их позже.
  • Вам нужно удалить все файлы .m из вашего приложения из тестового объекта. Вы можете либо сделать это, выбрав все .m файлы и удалив тестовую цель в инспекторе файлов Xcode, либо вы можете использовать фазу сборки источников компиляции тестовой цели.
  • Измените "пути поиска Framework" для вашей тестовой цели. Для Xcode 5 они должны быть $(SDKROOT)/Developer/Library/Frameworks $(inherited) $(DEVELOPER_FRAMEWORKS_DIR) в этом порядке и без дополнительных кавычек или обратных косых черт
  • Перейдите в общую область настроек тестовой целевой сборки и выберите свою цель из выпадающего меню. Если в меню уже указано целевое приложение, вы должны его выключить и снова включить. Это заставит Xcode изменить настройки загрузчика Bundle и Test Host с правильным значением.
  • Наконец, дважды проверьте схему своего приложения. В схеме выпадающего выберите схему редактирования. Затем нажмите тестовое действие. Убедитесь, что вы проверяете цель в списке на информационной панели и убедитесь, что все тесты выбраны.

Эта информация более или менее исходит из вышеупомянутой связанной документации, но я обновил шаги для Xcode 5.

EDIT:

Hmm 100% обратите внимание на то, что eph515 говорит о том, что символы отладки видны, но вы также можете проверить, что кто-то не установил ваше тестовое действие схемы для сборки в Release или другой конфигурации. Нажмите селектор схемы и выберите схему редактирования. Нажмите тестовое действие, а затем убедитесь, что Конфигурация сборки Debug

build configuration screen for test action in a scheme

Если у вас есть цель статической библиотеки

Итак, если у вас есть статическая целевая библиотека, у вас есть два варианта: 1. Логические тесты 2. Тесты приложений в хост-приложении.

Для 1. вы должны убедиться, что Bundle Loader и Test Host пустые для цели статической библиотеки. Затем ваши источники должны быть скомпилированы в тестовую цель, поскольку у них не было бы другого способа запуска.

Для 2. Вам нужно создать новый проект приложения в Xcode и добавить проект статической библиотеки в качестве подпроекта. Затем вам нужно вручную скопировать настройки сборки Bundle Loader и Test Host с вашего целевого объекта тестирования нового приложения на ваш целевой объект Static Lib. Затем вы открываете схему для своего нового тестового приложения и добавляете свою тестовую цель к действию тестов для нового приложения. Чтобы запустить тесты в вашей библиотеке, вы запускаете тестовое действие для своего хост-приложения.

Ответ 2

В Xcode 6 я смог исправить эту проблему, установив флажок "Разрешить тестирование API-интерфейсов хост-приложений" в целевой тесте > Общие > Тестирование.

Xcode Screenshot

Ответ 3

Я столкнулся с этим и воспользовался рекомендацией Jackslash, но с еще одним дополнением: выберите свою главную цель и найдите символы, скрытые по умолчанию (под Apple LVM 5.0 - Code Generation), если значение "Да", измените его на "Нет". Кажется, что он "скрывает" все символы скомпилированных источников, которые ищет цель unit test. Работает на меня. Пожалуйста, убедитесь, что вы включили все шаги, которые также были показаны на рисунке.

Ответ 4

Ответ был сочетанием ответов jackslash и eph515.

Как и в eph515, ответ symbols hidden by default должен быть "Нет" для отладки.

enter image description here

Также deployment postprocessing должен быть Нет для отладки.

enter image description here

Также все библиотеки, включенные в тестовую цель, должны быть удалены из unit test. Все, что должно быть оставлено, - это 3 в скриншоте плюс все, что характерно для модульного тестирования.

enter image description here

Также, если в конце списка есть фаза сборки сборки script, тогда она должна быть удалена (поскольку это артефакт модульного тестирования).

Затем сделайте все в ответ jackslash.

Ответ 5

В моем случае в Xcode 6.2 была ошибка в разных архитектурах в целевой цели проекта и тестах.

Цель проекта состоит только в архитектуре armv7 и armv7s (из-за некоторой старой библиотеки)

Целевая аудитория проекта имеет архитектуры armv7, armv7s и arm64.

Удаление архитектуры arm64 разрешает эту проблему для моего случая.

Project Editor -> Project Tests target -> Build Settings -> Valid Architectures = armv7 armv7s

(возможно, необходимо также установить "Архитектуры" вместо $(ARCHS_STANDARD) в $(ARCHS_STANDARD_32_BIT))

Ответ 6

Для меня это был всего лишь случай отсутствия тестовых целей для Схемы.

Для цели приложения перейдите в "Редактировать схему", затем нажмите "Тестирование" с правой стороны, затем добавьте тестовую цель с помощью кнопки "+" внизу: введите описание изображения здесь