2016-07-20 2 views
0

현재 SUT (System Under Test)가 외부 시스템에 요청을 제대로 보내고 있는지 확인하고 싶습니다. QA 환경에서 외부 모의 (HTTP를 통해) 및 메시지 대기열을 사용하고 있습니다 (구성을 통해 실제 시스템에서 교환 할 수 있음).Thread.sleep없이 비동기 테스트

문제점 : 이러한 외부 시스템에 대한 어설 션을 수행하기 위해 통합 테스트에서 thread.sleep()을 전파하고 있습니다. 이 질문을 콜백으로 대체하고 해당 요청에 대한 리스너를 제공하는 테스트 슈트로 바꾸고 싶습니다. 이미 이것에 대한 해결책이 있습니까?

+1

[Awaitility] (https://github.com/awaitility/awaitility) 및 [ConcurrentUnit] (https://github.com/jhalterman/concurrentunit)의 두 가지 도구가 도움이 될 수 있습니다. – Jonathan

답변

1

설명하는 시나리오에서 클래스 java.util.concurrent.CyclicBarrier의 인스턴스 사용을 권장 할 수 있습니다 - 일부 계산을 비동기 적으로 수행하는 다음 간단한 예제를 참조하십시오 (여기서는 "백분율 완료"값을 50으로 설정 한 다음 ~ 100), "percentage done"값을 주 스레드에서 검색 할 수 있습니다. 나는 내 SUT (또는, 당신이 좋아하는 AOP 프레임 워크를 사용할 수있는)의 두 가지 계산 방법을 가로 채기 위해 Mockito 스파이로의 JUnit 4를 사용

public class AsyncProcess implements Runnable { 

    private int percentageDone = 0; 

    public int getPercentageDone() { return percentageDone; } 

    public void doFirstHalf() { percentageDone = 50; } 

    public void doSecondHalf() { percentageDone = 100; } 

    public void run() { 
    doFirstHalf(); 
    doSecondHalf(); 
    } 

} 

단위 테스트이 들어 :

다음은 SUT 클래스입니다.

import static org.hamcrest.Matchers.is; 
import static org.junit.Assert.assertThat; 
import static org.mockito.Mockito.doAnswer; 

import java.util.concurrent.CyclicBarrier; 

import org.junit.Test; 
import org.mockito.Mockito; 
import org.mockito.invocation.InvocationOnMock; 
import org.mockito.stubbing.Answer; 

public class AsyncProcessTest { 

    @Test 
    public void testExecute() throws Exception { 

    final CyclicBarrier firstHalfStarted = new CyclicBarrier(2); 
    final CyclicBarrier firstHalfFinished = new CyclicBarrier(2); 
    final CyclicBarrier secondHalfStarted = new CyclicBarrier(2); 
    final CyclicBarrier secondHalfFinished = new CyclicBarrier(2); 

    AsyncProcess process = Mockito.spy(new AsyncProcess()); 

    doAnswer(new Answer() { 
     public Object answer(InvocationOnMock invocation) throws Throwable { 
     firstHalfStarted.await(); 
     invocation.callRealMethod(); 
     firstHalfFinished.await(); 
     return null; 
     } 
    }).when(process).doFirstHalf(); 

    doAnswer(new Answer() { 
     public Object answer(InvocationOnMock invocation) throws Throwable { 
     secondHalfStarted.await(); 
     invocation.callRealMethod(); 
     secondHalfFinished.await(); 
     return null; 
     } 
    }).when(process).doSecondHalf(); 

    new Thread(process, "AsyncProcess").start(); 

    assertThat(process.getPercentageDone(), is(0)); 

    firstHalfStarted.await(); 
    firstHalfFinished.await(); 

    assertThat(process.getPercentageDone(), is(50)); 

    secondHalfStarted.await(); 
    secondHalfFinished.await(); 

    assertThat(process.getPercentageDone(), is(100)); 
    } 

} 

이 접근 방식을 사용하면 관련된 스레드의 실행 흐름을 매우 세밀하게 제어 할 수 있습니다. CyclicBarriers 쌍을 캡슐화하고 인터 셉션 호출을 추출하고 시간 초과 및 적절한 예외 처리를 지원함으로써 "마이크로 프레임 워크"로 쉽게 확장 할 수 있습니다.