简体   繁体   中英

Thread.sleep in JUnit test case

I'm writing a test case to test an object behaviour.

When the object is instantiated it must allow the call of a method let say call() only if it has been called within 500 ms, otherwise it must throw an exception.

I designed the Junit test case like this:

@Test(expected = IllegalStateException.class)
public void testCallAfterTimeout() {
    MyObject o= new MyObject();
    //To ensure the timeout is occurred
    Thread.sleep(1000);
    o.call();
}

Do you think is a good practice or I should follow another approach?

Many thanks

There are two problems with using (real) time in test cases:

  1. It is never really deterministic. Especially when you are looking for high precision, testcases will succeed 95% of the time. But sometimes they fail, these are the hardest types of failure to debug. Note that when using Thread.sleep() with in a multithreaded test case this is even more difficult.
  2. Test cases with sleeps take long to run, after a while this will make running your full testset cumbersome.

If you must, your way is acceptable. But there are other options:

Don't use a real clock. Instead use a fake (mocked/stubbed) clock that you can control from your testcase:

@Test(expected = IllegalStateException.class)
public void testCallAfterTimeout() {
    MyObject o= new MyObject();
    // Example function that you could make
    advanceClock(1000, TimeUnit.MILLISECONDS)
    o.call();
}

In your object you have to inject a clock. MyObject could look like this:

class MyObject
{

     public MyObject()
     {
           this(new Clock());
     }

     // Protected so only the test case can access it
     protected MyObject(Clock clock)
     {
           // Save clock as local variable, store creation time etc.
     }
} 

In Java 8 a mechanism for this is provided, see for instance LocalDate.now() . But you can also implement your own quiet easily.

Regardless of what is the point of it and that you are asking for trouble ....

What if you decide that timeout needs to occure after 60minutes, will you wait for your test one hour? The timeout should be parameter of your MyObject so you can set it to some small value for testing (or even 0 to force alwyas a timeout when testing).

Secondly, if you want to really have testable time-related functions, the time and timeout should be handled separately from your main logic (MyObject class). You could for example have Timekeeper class with mehtod canCallMethod() which is invoked by your MyObject class (and it is set upon its construction). In that way in your test, you initlize your MyObject class with your own Timekeeper implementation that returns true or false and verify that your MyObject class behaves as expected. MyObject can have default constructor that always uses the 'real' timekeep so external world is not force to deal with Timekeeper internals.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM