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


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

#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.ч не часть тестовой цели, но это компилирует и запускает тесты без проблем.

Я думаю, это потому, что приложение строится сначала (как зависимость), а затем тесты. Затем компоновщик выясняет, что такое класс 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

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


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

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

  1. нет никакой разницы в выводе компоновщика (запуск команды с Ld).
  2. нет никакой разницы в целевых зависимостях (есть 1 зависимость от тестовой цели, которая является приложением)
  3. нет никакой разницы в настройках линкера.
6 66

6 ответов:

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

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

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

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

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

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

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

EDIT:

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

build configuration screen for test action in a scheme

если у вас есть статическая библиотека Target

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

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

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

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

Xcode Screenshot

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

ответ был комбинацией ответов jackslash и eph515.

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

enter image description here

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

enter image description here

также все библиотеки, включенные в целевой объект тестирования, должны быть удалены из модульного теста. Все, что должно быть оставлено, - это 3 На снимке экрана плюс все, что относится к единице тестирование.

enter image description here

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

тогда делай все в jackslash это.

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

цель проекта имеет только архитектуры armv7 и armv7s (из-за некоторых старых библиотек)

цель тестов проекта имеет архитектуры armv7, armv7s и arm64.

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

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

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

для меня это был просто случай отсутствия тестовых целей, добавленных для схемы.

для цели приложения перейдите к редактированию схемы, затем нажмите тест с правой стороны, затем добавьте тестовую цель с помощью кнопки + внизу: enter image description here