[java] JUnit 테스트를 기다리려면 어떻게해야합니까?

동 기적으로 일정 시간 동안 기다리려는 JUnit 테스트가 있습니다. 내 JUnit 테스트는 다음과 같습니다.

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    // WAIT FOR 2 SECONDS
    assertNull(sco.getIfNotExipred("foo"));
}

시도 Thread.currentThread().wait()했지만 예상대로 IllegalMonitorStateException이 발생 합니다.

트릭이 있습니까 아니면 다른 모니터가 필요합니까?



답변

어때요 Thread.sleep(2000);? 🙂


답변

Thread.sleep ()은 대부분의 경우에 작동 할 수 있지만 일반적으로 기다리는 경우 실제로 특정 조건이나 상태가 발생하기를 기다리고 있습니다. Thread.sleep ()은 당신이 기다리는 것이 실제로 일어 났음을 보장하지 않습니다.

예를 들어 휴식 요청을 기다리는 경우 일반적으로 5 초 후에 반환되지만 수면을 5 초로 설정하면 요청이 10 초 후에 다시 돌아 오는 날 테스트가 실패합니다.

이 문제를 해결하기 위해 JayWay에는 진행 하기 전에 특정 상태가 발생하도록 보장하는 Awatility 라는 훌륭한 유틸리티 가 있습니다.

유창한 API도 있습니다.

await().until(() ->
{
    return yourConditionIsMet();
});

https://github.com/jayway/awaitility


답변

정적 코드 분석기 (예 : SonarQube)가 불만을 제기하지만 잠자기보다는 다른 방법을 생각할 수없는 경우 다음과 같은 해킹을 시도 할 수 있습니다.
Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true);
개념적으로 잘못되었지만 Thread.sleep(1000).

물론 가장 좋은 방법은 true내가 가지고있는 대신 적절한 조건으로 Callable을 전달하는 것입니다.

https://github.com/awaitility/awaitility


답변

내부적으로 Thread.sleep을 사용하는 java.util.concurrent.TimeUnit 라이브러리를 사용할 수 있습니다. 구문은 다음과 같습니다.

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);

    TimeUnit.MINUTES.sleep(2);

    assertNull(sco.getIfNotExipred("foo"));
}

이 라이브러리는 시간 단위에 대한보다 명확한 해석을 제공합니다. ‘HOURS’/ ‘MINUTES’/ ‘SECONDS’를 사용할 수 있습니다.


답변

여기에CountDownLatch 설명 된 것과 같은 객체를 사용할 수도 있습니다 .


답변

일반적인 문제가 있습니다. 시간을 조롱하기가 어렵습니다. 또한 단위 테스트에 장기 실행 / 대기 코드를 배치하는 것은 정말 나쁜 습관입니다.

그래서 스케줄링 API를 테스트 가능하게 만들기 위해 다음과 같은 실제 및 모의 구현이있는 인터페이스를 사용했습니다.

public interface Clock {

    public long getCurrentMillis();

    public void sleep(long millis) throws InterruptedException;

}

public static class SystemClock implements Clock {

    @Override
    public long getCurrentMillis() {
        return System.currentTimeMillis();
    }

    @Override
    public void sleep(long millis) throws InterruptedException {
        Thread.sleep(millis);
    }

}

public static class MockClock implements Clock {

    private final AtomicLong currentTime = new AtomicLong(0);


    public MockClock() {
        this(System.currentTimeMillis());
    }

    public MockClock(long currentTime) {
        this.currentTime.set(currentTime);
    }


    @Override
    public long getCurrentMillis() {
        return currentTime.addAndGet(5);
    }

    @Override
    public void sleep(long millis) {
        currentTime.addAndGet(millis);
    }

}

이를 통해 테스트에서 시간을 모방 할 수 있습니다.

@Test
public void testExipres() {
    MockClock clock = new MockClock();
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    clock.sleep(2000) // WAIT FOR 2 SECONDS
    assertNull(sco.getIfNotExpired("foo"));
}

에 대한 고급 멀티 스레딩 모의 Clock는 물론 훨씬 더 복잡하지만 ThreadLocal, 예를 들어 참조 및 좋은 시간 동기화 전략을 사용하여 만들 수 있습니다 .


답변

테스트에서 지연을 생성하는 것이 절대적으로 필요하다면 CountDownLatch간단한 해결책입니다. 테스트 클래스에서 다음을 선언하십시오.

private final CountDownLatch waiter = new CountDownLatch(1);

필요한 경우 테스트에서 :

waiter.await(1000 * 1000, TimeUnit.NANOSECONDS); // 1ms

말할 필요도 없을 수도 있지만 대기 시간을 작게 유지하고 너무 많은 장소에 대기를 누적하지 않아야한다는 점을 명심하십시오.