2012-09-17 3 views
13

이 질문에 대한 해결책을 제시하는 다른 게시물을 읽었습니다. 그러나 솔루션을 테스트하려면 해시 코드를 내 애플리케이션에 추가해야합니다. 나에게 청소 코드는 단위 테스트보다 중요하다.목표 C - 단위 테스트 dispatch_async 블록?

나는 정기적으로 내 응용 프로그램에서 dispatch_async를 사용하며 단위 테스트에 문제가 있습니다. 문제는 내 테스트가 이미 완료된 후 블록이 실행되는 것입니다. 메인 큐에서 비동기 적으로 실행되기 때문입니다. 어떻게 든 블록이 실행될 때까지 기다린 다음 테스트를 계속할 수있는 방법이 있습니까?

난 단지 때문에 단위 테스트 dispatch_group

- (viod)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Test passes on this 
    [self.serviceClient fetchDataForUserId:self.userId]; 


    // Test fails on this because it's asynchronous 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self.serviceClient fetchDataForUserId:self.userId]; 
    }); 
} 

- (void)testShouldFetchUserDataUsingCorrectId 
{ 
    static NSString *userId = @"sdfsdfsdfsdf"; 
    self.viewController.userId = userId; 
    self.viewController.serviceClient = [[OCMockObject niceMockForClass:[ServiceClient class]]; 

    [[(OCMockObject *)self.viewController.serviceClient expect] fetchDataForUserId:userId]; 
    [self.viewController view]; 
    [(OCMockObject *)self.viewController.serviceClient verify]; 
} 

답변

37

는 비동기 블록 호출 할 수 있도록 메인 루프 짧게을 실행

- (void)testShouldFetchUserDataUsingCorrectId { 
    static NSString *userId = @"sdfsdfsdfsdf"; 
    self.viewController.userId = userId; 
    self.viewController.serviceClient = [[OCMockObject niceMockForClass:[ServiceClient class]]; 

    [[(OCMockObject *)self.viewController.serviceClient expect] fetchDataForUserId:userId]; 
    [self.viewController view]; 
    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 
    [(OCMockObject *)self.viewController.serviceClient verify]; 
} 

난이 무겁게로드 시스템에 실패하거나 수있는 생각을 다른 물건 (타이머 또는 다른 블록의 무리가있는 경우) 메인 스레드가. 이 경우 실행 루프를 오래 실행해야합니다 (테스트 케이스 속도가 느려짐). 또는 mock 객체의 기대치가 충족되거나 제한 시간에 도달 할 때까지 반복적으로 실행해야합니다 (mock 객체에 메소드를 추가해야 함). 기대에 부합하는지 여부를 묻는다).

+0

이 코드를 사용하면 테스트가 완료된 후에도 블록이 전혀 호출되지 않습니다. – aryaxt

+0

답변을 수정했습니다. 나는이 접근법을 더 철저하게 테스트했다. –

+0

감사합니다. 이 방법은 신뢰할만한 것으로 보이지 않지만 더 나은 솔루션을 찾지 못했기 때문에 테스트를 중단하지 않기를 바란다. – aryaxt

10

랩 실행을 블록에 완료를 통과 한 후 모든 통해 블록을 파견 실행 완료 그룹에 대한 기다리지 않으 dispatch_group_wait().

+0

매우 흥미 롭습니다. 전에는 dispatch_group을 들어 보지 못했습니다. NSOperationQueues를 다시 사용하지 않을 수도 있습니다. GCD와 관련된 유일한 문제는이 기능이 부족하다는 것입니다. – aryaxt

+0

@aryaxt 그래, 파견 그룹은 매우 유용하지만 드물게 어디에서나 언급됩니다 (심지어 공식 Apple 문서도 그다지 장황하지 않습니다) – JustSid

+0

이것은 매우 유용한 기술입니다. 더 많은 업을 받아야합니다. – e2l3n

0

마찬가지로 dispatch_async을 호출하는 유사한 메서드 서명을 사용하여 dispatch_async 래퍼를 만듭니다. 종속성은 랩퍼를 프로덕션 클래스에 삽입하여 사용합니다.

그런 다음 모조 래퍼를 만듭니다.이 래퍼는 대기열에있는 블록을 기록하고 모든 대기열에있는 블록을 동 기적으로 실행하기위한 추가 방법을 제공합니다. 실행중인 블록이 더 많은 블록을 대기열에 넣으면 재귀 적 "차단"을 수행 할 수 있습니다.

단위 테스트에서 테스트중인 시스템에 모의 래퍼를 삽입하십시오. SUT가 비동기 작업을한다고 생각하더라도 모든 것을 동기식으로 수행 할 수 있습니다.

도 좋은 솔루션처럼 들리지만 프로덕션 클래스는 대기열에있는 블록의 끝에 디스패치 그룹을 핑 (ping)하는 것을 "알아야"합니다.