2012-08-15 2 views
5

OCUnit을 사용하여 어플리케이션을 테스트 할 때 테스트를 실행하기 전에 AppDelegate, window 및 rootViewController를 평소와 같이 설정합니다. 내 rootViewController는 NSNotifications에 대한 옵저버로 자신을 추가합니다.단위 테스트를 실행할 때 app이 viewcontroller를 생성하지 못하도록하십시오.

격리 된 테스트 인스턴스 및 모의 관찰자로 이러한 알림을 테스트하면 자동으로 생성 된 rootViewController의 알림 처리기가 호출되어 일부 테스트가 실패하게됩니다.

OCUnit이 rootViewController를 작성하지 못하도록하거나 테스트 모드로 실행할 때 다른 ViewController 클래스를 사용하게 할 수 있습니까? 내 앱 코드에 특별한 테스트 관련 코드를 작성하지 않고도이 작업을 수행 할 수 있다면 멋지다.

답변

8

업데이트 : 오늘의 행동은 아래의 답변과 약간 다릅니다. How to Easily Switch Your App Delegate for Testing

앱 코드에 약간의 테스트 관련 코드를 추가해야합니다. 여기 내 전체 시작 순서 피하기 위해 할 수있는 작업은 다음과 같습니다

편집

  • 는 "테스트"에서 테스트 작업을
  • 을 선택하고 계획을
  • 안함 "을 실행 사용하여 인수 탭을 선택합니다 조치 옵션 "
  • 환경 변수를 추가하여 설정하십시오. runningTestsYES

편집 앱 위임

  • 은 즉시가 합리적으로 -application:didFinishLaunchingWithOptions:에 다음을 추가

    #if DEBUG 
        if (getenv("runningTests")) 
         return YES; 
    #endif 
    
  • -applicationDidBecomeActive: 단순히 return에 대해 동일한 작업을 수행합니다.

+0

좋은 해결책입니다. 감사! –

+0

솔루션을 제공해 주셔서 감사합니다. 나는 그것을 정말로 좋아하고 항상 그것을 사용한다. 나는 작은 개선 (또는 그렇게 생각한다)을 생각해 냈다. 내 대답을 한번 보게되면 기뻐할거야. 어쩌면 당신은 똑같은 것을 달성하는 더 좋은 방법을 안다. 아니면 내 생각을 좋아할 수도 있습니다 :) – FreeNickname

+0

그럼 왜 안되겠습니까? 나는 appdelegate를 조롱하는 것이 도움이 될 것이라고 제안한다. –

1

@ 존 리드의 솔루션은 중대하다, 나는 지금 내 모든 프로젝트에서 그것을 사용하지만, 그것으로 작은 문제가있다 : 계획은 기본적으로 버전 관리 시스템에 보관되지 않습니다. 따라서 git에서 프로젝트를 복제하면 runningTests 환경 변수가 설정되지 않았기 때문에 테스트가 실패 할 수 있습니다. 그리고 저는 항상 잊고 있습니다.

그래서 그것에 대해 자신을 생각 나게하는 것은, 지금 내 모든 프로젝트에 작은 테스트를 추가

#import <UIKit/UIKit.h> 
#import <XCTest/XCTest.h> 

@interface DMAUnitTestModeTests : XCTestCase 

@end 

@implementation DMAUnitTestModeTests 

- (void)testUnitTestMode { 
    BOOL isInUnitTestMode = (BOOL)getenv("runningTests"); 

    XCTAssert(isInUnitTestMode, @"You have to set a 'runningTests' environment variable in the schemes editor."); 
    //http://stackoverflow.com/questions/11974138/prevent-app-from-creating-a-viewcontroller-when-running-unit-tests/11981192#11981192 
} 

@end 

을 누군가가 더 나은 솔루션을 함께 제공하는 경우 알려 주시기 바랍니다 :

왜 내가 대답으로 게시 : 이것은 @ 존 리드의 대답에 대한 약간의 개선 (이는 정말 좋아)입니다. 저는 이것을 주석으로 쓰고 싶었지만이 방법으로 코드를 공유하는 것은 불편했을 것입니다. 그래서 나는 질문을 정확히 답변하지는 않지만 답변으로 게시하기로 결정했습니다.

1

Xcode 자체가 테스트를 실행할 때 환경 변수를 설정하므로 스키마에서 아무 것도 만들 필요가 없습니다. 이미 다른 목적으로 그렇게하고 있다면 그렇게하는 것이 실용적 일 수 있습니다. 그러나 테스트 실행 여부를 결정하기 위해 Xcode의 환경 변수를 사용할 수 있습니다.

옵션 1 :

static BOOL isRunningTests(void) __attribute__((const)); 

static BOOL isRunningTests(void) 
{ 
    NSDictionary* environment = [[NSProcessInfo processInfo] environment]; 
    NSString* injectBundle = environment[@"XCInjectBundle"]; 
    NSLog(@"TSTL %@", [injectBundle pathExtension]); 
    return [[injectBundle pathExtension] isEqualToString:@"xctest"] || [[injectBundle pathExtension] isEqualToString:@"octest"]; 
} 

그리고 단순히 전화 isRunningTests() 당신이 테스트를 확인해야 할 때마다 코드의 대부분은 앱 위임에 던질 수있는 objc에서이처럼 보인다. 이 코드는, 그러나, 정말 TestHelper 클래스, 예를 들어, 다른 곳에 저장해야합니다

옵션 2 :

우리는 여전히 글로벌 변수를 사용하는
// TestHelper.h 
#import <Foundation/Foundation.h> 

extern BOOL isRunningTests(void) __attribute__((const)); 
// TestHelper.m 
#import "TestCase.h" 

extern BOOL isRunningTests(void) 
{ 
    NSDictionary* environment = [[NSProcessInfo processInfo] environment]; 
    NSString* injectBundle = environment[@"XCInjectBundle"]; 
    NSLog(@"TSTL %@", [injectBundle pathExtension]); 
    return [[injectBundle pathExtension] isEqualToString:@"xctest"] || [[injectBundle pathExtension] isEqualToString:@"octest"]; 
} 

주 , 그리고 클래스 이름의 선택은 실제로 부적합하다. 그것을 유지하는 것이 의미가있는 클래스입니다.

옵션 3 :

그리고 빠른에, 당신은 목표 - C와 신속한 모두에서 작동하기 위해 클래스에 포장해야합니다. 이처럼 할 수있는 :

class TestHelper: NSObject { 
    static let isRunningTests: Bool = { 
     guard let injectBundle = NSProcessInfo.processInfo().environment["XCInjectBundle"] as NSString? else { 
      return false 
     } 
     let pathExtension = injectBundle.pathExtension 

     return pathExtension == "xctest" || pathExtension == "octest" 
    }() 
} 
1

내가 RxTodo MVVM example app에서 본 가장 깨끗한 방법은, 그것은 다음과 같이 진행됩니다

  1. 제거 @UIApplication 속성을
  2. main.swift 추가 응용 프로그램 위임 클래스에서 이 같은 구현 파일 :

    import UIKit 
    import Foundation 
    
    final class MockAppDelegate: UIResponder, UIApplicationDelegate {} 
    
    private func appDelegateClassName() -> String { 
        let isTesting = NSClassFromString("XCTestCase") != nil 
        return 
        NSStringFromClass(isTesting ? MockAppDelegate.self : AppDelegate.self) 
    } 
    
    UIApplicationMain(
        CommandLine.argc, 
        UnsafeMutableRawPointer(CommandLine.unsafeArgv) 
         .bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)), 
        NSStringFromClass(UIApplication.self), appDelegateClassName() 
    ) 
    

It 's Swift 3 버전입니다. v2의 경우 편집 내역을 참조하십시오.

관련 문제