Подтвердить что ты не робот

Xcode: TEST против макросов препроцессора DEBUG

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

Должен ли я различать схемы Run (Command-R) и Test (Command-U)?

I.e., следует ли мне создать новую конфигурацию сборки под названием "Тест", добавить к ней макрос TSP = 1 препроцессора и использовать его в качестве конфигурации сборки для схемы тестирования? Или, должен ли я просто продолжать работать и тестировать как Debug?

Я исхожу из фона Ruby/Rails, где обычно есть среда тестирования, разработки и производства. Мне кажется, что Debug похож на разработку, а Release - как на производство, но мы пропускаем тест, поэтому я думаю, что имеет смысл добавить Test.

Комментарии? Мнения? Предложения?

Я специально спрашиваю об этом, потому что хочу скомпилировать что-то для теста с помощью:

#ifdef TEST
// Do something when I test.
#endif

Я не думаю, что это важно, если я также скомпилирую это для Debug. Итак, я действительно мог просто сделать:

#ifdef DEBUG
// Do something when I run or test.
#endif

Но на самом деле я намереваюсь сделать это только для тестов. Итак, почему я думаю, что я должен отличать отладку и тест, но интересно, почему Xcode не делает этого по умолчанию? Думает ли Apple, что вы не должны различать их?

4b9b3361

Ответ 1

Вместо создания конфигурации тестовой сборки I:

  • создан файл Tests-Prefix.pch:

    #define TEST 1
    #import <SenTestingKit/SenTestingKit.h>
    #import "CocoaPlant-Prefix.pch"
    
  • введите свой путь в поле Prefix Header настроек целевой сборки тестов.

  • добавлен следующий код в начало созданного мной файла с именем MyAppDefines.h, импортированного в MyApp-Prefix.pch:

    #ifdef TEST
    #define TEST_CLASS NSClassFromString(@"AppDelegateTests") // any test class
    #define BUNDLE [NSBundle bundleForClass:TEST_CLASS]
    #define APP_NAME @"Tests"
    #else
    #define BUNDLE [NSBundle mainBundle]
    #define APP_NAME [[BUNDLE infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]
    #endif
    

Это позволяет мне использовать BUNDLE, где я имею в виду [NSBundle mainBundle], а также работать, когда я запускаю тесты.

Импорт SenTestingKit в Tests-Prefix.pch также ускоряет компиляцию структуры SenTestingKit и позволяет мне оставить #import <SenTestingKit/SenTestingKit.h> сверху всех файлов тестов.

Ответ 2

Макросы препроцессора не будут работать, вам нужно проверять среду во время выполнения.

Objective-C

static BOOL isRunningTests(void)
{
    NSDictionary* environment = [[NSProcessInfo processInfo] environment];
    return (environment[@"XCTestConfigurationFilePath"] != nil);
}

Swift

var unitTesting : Bool 
{
    return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
}

(обновлено для Xcode 11)

Ответ 3

Возможно, вы захотите добавить новую конфигурацию сборки.

В xcode 4 нажмите на свой проект в навигаторе слева.

В главном окне нажмите на свой проект, а затем выберите вкладку "информация".

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

Теперь нажмите на свою цель и перейдите на вкладку настроек сборки.

Поиск макросов препроцессора

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

Просто дважды щелкните по вашей новой "тестовой" конфигурации и добавьте TESTING = 1.

Наконец, отредактируйте схему сборки. Выберите параметры тестирования для вашей схемы. Должно быть раскрывающееся меню "Конфигурация сборки". Выберите свою "тестовую" конфигурацию.

Ответ 4

Я решил добавить проверку для переменной среды в самом коде, вместо использования предложения isRunningTests(), которое сделал Роберт.

  • Отредактируйте текущую схему (Product/Scheme/Edit Scheme) или Command + <
  • Нажмите "Конфигурация теста"
  • Нажмите "Аргументы" Снимите флажок "Использовать аргументы" Выполнить действие "и переменные среды
  • Разверните раздел "Переменная среда" и добавьте переменную TESTING со значением YES
  • Добавьте это к своему коду где-нибудь и вызовите, когда вам нужно:
 + (BOOL) isTesting
    {
        NSDictionary* environment = [[NSProcessInfo processInfo] environment];
        return [environment objectForKey:@"TESTING"] != nil;
    }

Экран должен выглядеть так, как только вы закончите.

The screen should look like this

Приведенный выше код найдет переменную среды TESTING при работе в тестовом режиме или режиме приложения. Этот код идет в вашем приложении, а не в файлах unit test. Вы можете использовать

#ifdef DEBUG
...
#endif

Чтобы предотвратить выполнение кода в процессе производства.

Ответ 5

Роберт ответит в SWIFT 3.0:

func isRunningTests() -> Bool {
    let environment = ProcessInfo().environment
    return (environment["XCInjectBundleInto"] != nil);
}

Ответ 6

Если вы создадите конфигурацию тестовой сборки и затем установите для свойства "Другие флаги Swift" вашей цели значение "-DTEST", это определит макрос TEST, который будет работать в вашем быстром коде. Убедитесь, что вы установили его в настройках сборки целевого приложения, чтобы использовать его в своем коде App Swift.

Other Swift Flags setting for DEBUG and TEST

Затем с помощью этого набора вы можете проверить свой код следующим образом:

func testMacro() {
   #if !TEST 
       // skipping over this block of code for unit tests
   #endif
}

Ответ 7

Я тестирую такое долгое время, нашел результат:

Не только вы добавляете макрос preproessor в свою цель unit test (у вас может быть много методов используя переменные для модульного тестирования только, и следуйте методам @MattDiPasquale),

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

Надеюсь, что это поможет вам.

Ответ 8

Обновлено для Xcode10:

static let isRunningUnitTests: Bool = {
    let environment = ProcessInfo().environment
    return (environment["XCTestConfigurationFilePath"] != nil)
}()

Ответ 9

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

+ (BOOL)isRunningTests {
   static BOOL runningTests;
   static dispatch_once_t onceToken;

   // Only check once
   dispatch_once(&onceToken, ^{
      NSDictionary* environment = [[NSProcessInfo processInfo] environment];
      NSString* injectBundle = environment[@"XCInjectBundle"];
      NSString* pathExtension = [injectBundle pathExtension];
      runningTests = ([pathExtension isEqualToString:@"octest"] ||
                      [pathExtension isEqualToString:@"xctest"]);
   });
   return runningTests;
}

Ответ 10

В iOS, [UIApplication sharedApplication] вернет nil, когда выполняется unit test.

Ответ 11

Модифицированная версия ответа Kev, которая работает для меня на Xcode 8.3.2

+(BOOL)isUnitTest {

    static BOOL runningTests;
    static dispatch_once_t onceToken;

    // Only check once
    dispatch_once(&onceToken, ^{
        NSDictionary* environment = [[NSProcessInfo processInfo] environment];
        if (environment[@"XCTestConfigurationFilePath"] != nil && ((NSString *)environment[@"XCTestConfigurationFilePath"]).length > 0) {
            runningTests = true;
        } else {
            runningTests = false;
        }
    });
    return runningTests;
}