ฉันจะทำการทดสอบ JUnit รอได้อย่างไร


95

ฉันมีการทดสอบ 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 (ตามที่คาดไว้)

มีเคล็ดลับบางอย่างหรือฉันต้องการจอภาพอื่น?

คำตอบ:


119

แล้วไงThread.sleep(2000);? :)


15
หากคุณใช้เครื่องมือวิเคราะห์โค้ดเช่น sonarqube พวกเขาจะบ่นเกี่ยวกับการThread.sleepพูดอะไรบางอย่างเช่น การใช้ Thread.sleep ในการทดสอบโดยทั่วไปเป็นความคิดที่ไม่ดี สร้างการทดสอบที่เปราะบางซึ่งอาจล้มเหลวโดยไม่อาจคาดเดาได้ขึ้นอยู่กับสภาพแวดล้อม ("ผ่านบนเครื่องของฉัน!") หรือโหลด อย่าพึ่งพาการกำหนดเวลา (ใช้การล้อเลียน) หรือใช้ไลบรารีเช่น Awaitility สำหรับการทดสอบแบบ asynchroneous
FuryFart

4
คำตอบนี้ควรถูกลบออกและถือว่าเป็นอันตราย คำตอบที่ดีกว่าด้านล่างคือstackoverflow.com/a/35163873/1229735
yiati

73

Thread.sleep () สามารถทำงานได้ในกรณีส่วนใหญ่ แต่โดยปกติแล้วหากคุณกำลังรอคุณกำลังรอให้เกิดเงื่อนไขหรือสถานะเฉพาะ Thread.sleep () ไม่รับประกันว่าสิ่งที่คุณรอคอยจะเกิดขึ้นจริง

หากคุณกำลังรอคำขอพักตัวอย่างเช่นปกติแล้วอาจจะกลับมาใน 5 วินาที แต่ถ้าคุณตั้งเวลานอนเป็นเวลา 5 วินาทีในวันที่คำขอของคุณกลับมาใน 10 วินาทีการทดสอบของคุณจะล้มเหลว

ในการแก้ไข JayWay นี้มียูทิลิตี้ที่ยอดเยี่ยมที่เรียกว่าAwatilityซึ่งเหมาะอย่างยิ่งสำหรับการตรวจสอบว่ามีอาการใดเกิดขึ้นก่อนที่คุณจะดำเนินการต่อ

มี API ที่คล่องแคล่วดีเช่นกัน

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

https://github.com/jayway/awaitility


ฉันไม่สามารถรอการรวบรวมใน Android Studio ได้
IgorGanapolsky

คุณเพิ่มบรรทัดนี้ในไฟล์ gradle ของคุณ: compile 'org.awaitility: awaitility: 3.0.0' หรือไม่
Samoht

ไม่. คุณจะเพิ่มสิ่งนี้ในกรณีทดสอบที่คุณต้องการรอเงื่อนไขเฉพาะ
Ben Glasser

16

ในกรณีที่เครื่องวิเคราะห์รหัสคงที่ของคุณ (เช่น SonarQube) ร้องเรียน แต่คุณไม่สามารถคิดวิธีอื่นได้นอกจากการนอนหลับคุณอาจลองใช้แฮ็คเช่น Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true); มันไม่ถูกต้องตามแนวคิด แต่ก็เหมือนกับThread.sleep(1000)ไฟล์.

วิธีที่ดีที่สุดคือส่ง Callable โดยมีเงื่อนไขที่เหมาะสมมากกว่าtrueที่ฉันมี

https://github.com/awaitility/awaitility


@Jitendra: โปรดทราบว่า Awaitility ใช้ในการมีDurationระดับ แต่ตั้งแต่รุ่น 4 Durationsที่พวกเขาตั้งชื่อมันไป
Jacob van Lingen

ข้อแม้อย่างหนึ่งคือหากคุณต้องการให้ความล่าช้าของการสำรวจความคิดเห็นเป็น 10 วินาทีขึ้นไปอย่าลืมเพิ่มระยะหมดเวลาเป็นมากกว่า 10 วินาทีเนื่องจากนั่นคือการหมดเวลาเริ่มต้นและการรอคอยจะบ่นว่าการหมดเวลาน้อยกว่าความล่าช้าของการสำรวจความคิดเห็น
focus

15

คุณสามารถใช้ไลบรารี java.util.concurrent.TimeUnit ซึ่งใช้ Thread.sleep ภายใน ไวยากรณ์ควรมีลักษณะดังนี้:

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

    TimeUnit.MINUTES.sleep(2);

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

ไลบรารีนี้ให้การตีความที่ชัดเจนยิ่งขึ้นสำหรับหน่วยเวลา คุณสามารถใช้ 'HOURS' / 'MINUTES' / 'SECONDS'


ถ้าฉันเริ่มเธรดผู้ปฏิบัติงานจากภายในการทดสอบเธรดจะsleep()มีผลต่อเธรดผู้ปฏิบัติงานหรือไม่
Anthony Kong


0

มีปัญหาทั่วไปคือเวลาล้อเลียนมันยาก นอกจากนี้การวางโค้ดที่รัน / รอนานในการทดสอบหน่วยเป็นวิธีปฏิบัติที่แย่มาก

ดังนั้นสำหรับการสร้างกำหนดการทดสอบ 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การอ้างอิงและกลยุทธ์การซิงโครไนซ์เวลาที่ดีตัวอย่างเช่น


0

หากต้องสร้างความล่าช้าในการทดสอบCountDownLatchเป็นวิธีง่ายๆ ในชั้นเรียนทดสอบของคุณประกาศ:

private final CountDownLatch waiter = new CountDownLatch(1);

และในการทดสอบหากจำเป็น:

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

อาจจะไม่จำเป็นที่จะต้องพูด แต่จำไว้ว่าคุณควรรอเวลาให้น้อยและไม่ต้องรอนานเกินไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.