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

Как сообщить приложению, что его работающие тесты Unit в чистом проекте Swift?

Одна из неприятных вещей при запуске тестов в XCode 6.1 заключается в том, что все приложение должно запускаться и запускать свою раскадровку и rootController. В моем приложении это запускает некоторые серверные вызовы, которые извлекают данные API. Тем не менее, я не хочу, чтобы приложение выполняло это при выполнении своих тестов.

Когда препроцессорные макросы ушли, что лучше всего для моего проекта знать, что он был запущен с проверкой, а не обычным запуском? Я запускаю их обычно с CMD + U и ботом.

Псевдокод:

// Appdelegate.swift

if runningTests() {
   return
} else {
   // do ordinary api calls
}
4b9b3361

Ответ 1

Вместо проверки того, выполняются ли тесты для предотвращения побочных эффектов, вы можете запускать тесты без самого хост-приложения. Перейдите в Настройки проекта → выберите целевую аудиторию → Общие → Тестирование → Хост-приложение → выберите "Нет". Не забудьте указать все файлы, необходимые для запуска тестов, а также библиотеки, обычно включенные в целевую программу Host.

enter image description here

Ответ 2

Ответ Elvind не плох, если вы хотите иметь то, что раньше называлось чистыми "Logic Tests". Если вы все еще хотите запустить приложение, содержащее хост-приложение, но условно выполняете или не выполняете код в зависимости от того, запускаются ли тесты, вы можете использовать следующее, чтобы определить, был ли введен тестовый комплект:

if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
     // Code only executes when tests are running
}

Я использовал условный флаг компиляции, как описано в этом ответе, чтобы затраты времени исполнения возникали только в сборке отладки:

#if DEBUG
    if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil {
        // Code only executes when tests are running
    }
#endif

Изменить Swift 3.0

if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil {
    // Code only executes when tests are running
}

Ответ 3

Я использую это в приложении: didFinishLaunchingWithOptions:

// Return if this is a unit test
if let _ = NSClassFromString("XCTest") {
    return true
}

Ответ 4

Другое, на мой взгляд, проще:

Вы редактируете свою схему для передачи логического значения в качестве аргумента запуска в приложение. Вот так:

Установить аргументы запуска в Xcode

Все аргументы запуска автоматически добавляются в ваш NSUserDefaults.

Теперь вы можете получить BOOL как:

BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];

Ответ 5

Я считаю, что вполне законно хотеть узнать, работаете ли вы в тесте или нет. Существует множество причин, по которым это может быть полезно. Например, при выполнении тестов я возвращаюсь на ранней стадии из методов application-do/will-finish-launch-запуска в App Delegate, чтобы тесты запускались быстрее для кода, не связанного с моим unit test. Тем не менее, я не могу пройти чистую "логическую" проверку по ряду других причин.

Я использовал превосходную технику, описанную Майклом Макгуайром выше. Тем не менее, я заметил, что перестала работать для меня около Xcode 6.4/iOS8.4.1 (возможно, он сломался раньше).

А именно, я больше не вижу XCInjectBundle при запуске теста внутри тестовой цели для моей структуры. То есть, я запускаю внутри тестовой цели, которая тестирует фреймворк.

Итак, используя подход @Fogmeister, каждая из моих тестовых схем теперь устанавливает переменную среды, которую я могу проверить.

введите описание изображения здесь

Затем, вот некоторый код, который у меня есть для класса с именем APPSTargetConfiguration, который может ответить на этот простой вопрос для меня.

static NSNumber *__isRunningTests;

+ (BOOL)isRunningTests;
{
    if (!__isRunningTests) {
        NSDictionary *environment = [[NSProcessInfo processInfo] environment];
        NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"];
        __isRunningTests = @([isRunningTestsValue isEqualToString:@"YES"]);
    }

    return [__isRunningTests boolValue];
}

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

Ответ 6

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

Использование

if isRunningTests {
    return "lena.bmp"
}
return "facebook_profile_photo.bmp"

Ответ 7

Вот способ, который я использовал в Swift 4/Xcode 9 для наших модульных тестов. Он основан на ответе Джесси.

Нелегко предотвратить загрузку раскадровки, но если вы добавите это в начало didFinishedLaunching, то это очень ясно для ваших разработчиков, что происходит:

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions:
                 [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    #if DEBUG
    if let _ = NSClassFromString("XCTest") {
        // If we're running tests, don't launch the main storyboard as
        // it confusing if that is running fetching content whilst the
        // tests are also doing so.
        let viewController = UIViewController()
        let label = UILabel()
        label.text = "Running tests..."
        label.frame = viewController.view.frame
        label.textAlignment = .center
        label.textColor = .white
        viewController.view.addSubview(label)
        self.window!.rootViewController = viewController
        return true
    }
    #endif

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

Ответ 8

Вы можете передавать аргументы времени выполнения в приложение в зависимости от схемы здесь...

enter image description here

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

Ответ 9

Комбинированный подход @Jessy и @Michael McGuire

(Поскольку принятый ответ не поможет вам при разработке структуры)

Итак, вот код:

#if DEBUG
        if (NSClassFromString(@"XCTest") == nil) {
            // Your code that shouldn't run under tests
        }
#else
        // unconditional Release version
#endif

Ответ 10

Некоторые из этих подходов не работают с UITests, и если вы в основном тестируете сам код приложения (вместо добавления конкретного кода в цель UITest).

В итоге я установил переменную среды в методе test setUp:

XCUIApplication *testApp = [[XCUIApplication alloc] init];

// set launch environment variables
NSDictionary *customEnv = [[NSMutableDictionary alloc] init];
[customEnv setValue:@"YES" forKey:@"APPS_IS_RUNNING_TEST"];
testApp.launchEnvironment = customEnv;
[testApp launch];

Обратите внимание, что это безопасно для моего тестирования, поскольку в настоящее время я не использую другие значения launchEnvironment; Если вы это сделаете, вы, конечно, сначала захотите скопировать любые существующие значения.

Затем в коде приложения я ищу эту переменную среды, если/когда я хочу исключить некоторые функции во время теста:

BOOL testing = false;
...
if (! testing) {
    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
    NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"];
    testing = [isRunningTestsValue isEqualToString:@"YES"];
}

Примечание - спасибо за комментарий RishiG, который дал мне эту идею; Я просто расширил это до примера.

Ответ 11

Работал для меня:

Objective-C

[[NSProcessInfo processInfo].environment[@"DYLD_INSERT_LIBRARIES"] containsString:@"libXCTTargetBootstrapInject"]

Swift:   ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"]?.contains("libXCTTargetBootstrapInject") ?? false