Development Tip

순수한 Swift 프로젝트에서 실행중인 단위 테스트를 앱에 알리는 방법은 무엇입니까?

yourdevel 2020. 11. 9. 21:15
반응형

순수한 Swift 프로젝트에서 실행중인 단위 테스트를 앱에 알리는 방법은 무엇입니까?


XCode 6.1에서 테스트를 실행할 때 한 가지 성가신 점은 전체 앱이 스토리 보드와 루트 viewController를 실행하고 실행해야한다는 것입니다. 내 앱에서 이것은 API 데이터를 가져 오는 서버 호출을 실행합니다. 그러나 테스트를 실행할 때 앱이이 작업을 수행하는 것을 원하지 않습니다.

전 처리기 매크로가 사라진 상태에서 내 프로젝트가 일반 실행이 아닌 테스트 실행으로 실행되었음을 인식하는 가장 좋은 방법은 무엇입니까? CMD + U와 봇에서 정상적으로 실행합니다.

의사 코드는 다음과 같습니다.

// Appdelegate.swift

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

부작용을 피하기 위해 테스트가 실행 중인지 확인하는 대신 호스트 앱 자체없이 테스트를 실행할 수 있습니다. 프로젝트 설정으로 이동-> 테스트 대상 선택-> 일반-> 테스트-> 호스트 응용 프로그램-> '없음'을 선택합니다. 테스트를 실행하는 데 필요한 모든 파일과 일반적으로 Host 앱 대상에 포함 된 라이브러리를 포함해야합니다.

여기에 이미지 설명 입력


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
}

나는 이것을 application : didFinishLaunchingWithOptions에서 사용합니다.

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

기타, 내 생각에 더 간단한 방법 :

앱에 시작 인수로 부울 값을 전달하도록 스키마를 편집합니다. 이렇게 :

Xcode에서 시작 인수 설정

모든 시작 인수는 자동으로 NSUserDefaults.

이제 다음과 같이 BOOL을 얻을 수 있습니다.

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

테스트 내에서 실행 중인지 여부를 알고 싶은 것이 완전히 합법적이라고 생각합니다. 이것이 도움이 될 수있는 여러 가지 이유가 있습니다. 예를 들어 테스트를 실행할 때 App Delegate의 application-did / will-finish-launching 메서드에서 일찍 돌아와서 단위 테스트와 관련이없는 코드에 대해 테스트를 더 빠르게 시작합니다. 하지만 다른 여러 가지 이유로 순수한 "논리적"테스트를 할 수는 없습니다.

위의 @Michael McGuire가 설명한 뛰어난 기술을 사용했습니다. 그러나 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에서 허용하는 것처럼 기본 앱 체계에서 테스트를 실행하면 (즉, 테스트 체계 중 하나를 선택하지 않음)이 환경 변수 집합을 얻지 못한다는 것입니다.


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

용법

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

다음은 단위 테스트를 위해 Swift 4 / Xcode 9에서 사용한 방법입니다. Jesse의 답변을 기반으로 합니다.

스토리 보드가로드되는 것을 막는 것은 쉽지 않지만 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's 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

(앱이 정상적으로 시작되기를 원하는 UI 테스트에 대해 이와 같은 작업을 수행해서는 안됩니다!)


여기서 스키마에 따라 앱에 런타임 인수를 전달할 수 있습니다.

여기에 이미지 설명 입력

그러나 나는 그것이 실제로 필요한지 여부에 대해 의문을 제기합니다.


@Jessy와 @Michael McGuire의 결합 된 접근 방식

(허용되는 대답은 프레임 워크를 개발하는 동안 도움이되지 않습니다)

그래서 여기에 코드가 있습니다 :

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

Some of these approaches don't work with UITests and if you're basically testing with the app code itself (rather than adding specific code into a UITest target).

I ended up setting an environment variable in the test's setUp method:

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];

Note that this is safe for my testing since I don't currently use any other launchEnvironment values; if you do, you would of course want to copy any existing values first.

Then in my app code, I look for this environment variable if/when I want to exclude some functionality during a test:

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

Note - thanks for RishiG's comment that gave me this idea; I just expanded that to an example.


Worked for me:

Objective-C

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

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

참고URL : https://stackoverflow.com/questions/27500940/how-to-let-the-app-know-if-its-running-unit-tests-in-a-pure-swift-project

반응형