Bagaimana cara membuat pengujian JUnit menunggu?

95

Saya memiliki tes JUnit yang ingin saya tunggu selama beberapa waktu secara sinkron. Tes JUnit saya terlihat seperti ini:

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

Saya mencoba Thread.currentThread().wait(), tetapi itu melempar IllegalMonitorStateException (seperti yang diharapkan).

Apakah ada trik untuk itu atau apakah saya memerlukan monitor yang berbeda?

Kylar
sumber

Jawaban:

119

Bagaimana dengan Thread.sleep(2000);? :)

Muel
sumber
15
Jika Anda menggunakan alat analisis kode seperti sonarqube, mereka akan mengeluh tentang Thread.sleepmengatakan sesuatu seperti Menggunakan
FuryFart
4
Jawaban ini harus dihilangkan, dan dianggap merugikan. Jawaban yang jauh lebih baik di bawah ini adalah stackoverflow.com/a/35163873/1229735
yiati
73

Thread.sleep () dapat berfungsi di banyak kasus, tetapi biasanya jika Anda menunggu, Anda sebenarnya menunggu kondisi atau status tertentu terjadi. Thread.sleep () tidak menjamin bahwa apa pun yang Anda tunggu benar-benar terjadi.

Jika Anda menunggu permintaan istirahat misalnya mungkin biasanya kembali dalam 5 detik, tetapi jika Anda mengatur tidur Anda selama 5 detik pada hari permintaan Anda kembali dalam 10 detik pengujian Anda akan gagal.

Untuk memperbaiki JayWay ini memiliki utilitas hebat yang disebut Awatility yang sempurna untuk memastikan bahwa kondisi tertentu terjadi sebelum Anda melanjutkan.

Ini memiliki api fasih yang bagus juga

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

https://github.com/jayway/awaitility

Ben Glasser
sumber
Saya tidak bisa menunggu untuk dikompilasi di Android Studio.
IgorGanapolsky
Apakah Anda menambahkan baris ini ke file gradle: compile 'org.awaitility: awaitility: 3.0.0'?
Samoht
tidak. Anda akan menambahkan ini dalam kasus uji di mana Anda ingin menunggu kondisi tertentu
Ben Glasser
16

Jika penganalisis kode statis Anda (seperti SonarQube) mengeluh, tetapi Anda tidak dapat memikirkan cara lain, daripada tidur, Anda dapat mencoba dengan peretasan seperti: Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true); Ini secara konseptual salah, tetapi sama dengan Thread.sleep(1000).

Cara terbaik, tentu saja, adalah dengan memberikan Callable, dengan kondisi Anda yang sesuai, daripada true, yang saya miliki.

https://github.com/awaitility/awaitility

rastaman
sumber
@ Jitendra: Perhatikan bahwa Awaitility dulu memiliki Durationkelas, tetapi sejak versi 4 mereka menamainya menjadi Durations.
Jacob van Lingen
Satu peringatan, jika Anda ingin penundaan polling menjadi 10 detik atau lebih, pastikan untuk menambah waktu tunggu menjadi lebih dari 10 detik karena itu adalah batas waktu default dan awaitility akan mengeluh bahwa waktu tunggu kurang dari penundaan polling.
fokus
15

Anda dapat menggunakan pustaka java.util.concurrent.TimeUnit yang secara internal menggunakan Thread.sleep. Sintaksnya akan terlihat seperti ini:

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

    TimeUnit.MINUTES.sleep(2);

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

Perpustakaan ini memberikan interpretasi yang lebih jelas untuk satuan waktu. Anda dapat menggunakan 'HOURS' / 'MINUTES' / 'SECONDS'.

Akshay Pagar
sumber
Jika saya memulai thread pekerja dari dalam pengujian, apakah hal tersebut akan sleep()memengaruhi thread pekerja?
Anthony Kong
6

Anda juga bisa menggunakan CountDownLatchobjek seperti yang dijelaskan di sini .

Pierre Voisin
sumber
0

Ada masalah umum: sulit untuk mengejek waktu. Selain itu, praktik yang sangat buruk untuk menempatkan kode yang berjalan / menunggu dalam waktu lama dalam pengujian unit.

Jadi, untuk membuat API penjadwalan dapat diuji, saya menggunakan antarmuka dengan implementasi nyata dan tiruan seperti ini:

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);
    }

}

Dengan ini, Anda bisa meniru waktu dalam ujian Anda:

@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"));
}

Sebuah tiruan multi-threading tingkat lanjut untuk Clockjauh lebih kompleks, tentu saja, tetapi Anda dapat membuatnya dengan ThreadLocalreferensi dan strategi sinkronisasi waktu yang baik, misalnya.

Dávid Horváth
sumber
0

Jika itu adalah keharusan mutlak untuk menghasilkan penundaan dalam pengujian CountDownLatchadalah solusi sederhana. Di kelas pengujian Anda, nyatakan:

private final CountDownLatch waiter = new CountDownLatch(1);

dan dalam pengujian jika diperlukan:

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

Mungkin tidak perlu dikatakan tetapi perlu diingat bahwa Anda harus menjaga waktu tunggu kecil dan tidak mengumpulkan waktu tunggu terlalu banyak.

pirho
sumber