6

I have the following method and I am struggling to get 100% code coverage.

public final class SleepingHelper {
    public static void sleepInMillis(Duration timeOfNextTry) {
        try {
            Thread.sleep(timeOfNextTry.toMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The question is how can I force Thread.sleep to throw an exception?

Edit: since it was marked as duplicate, I am still wondering what I would assert in the test ? The other question Is more generic.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Paul Fournel
  • 10,807
  • 9
  • 40
  • 68
  • 5
    the focus on a 100% coverage is delusory. Testing this method does not give you any further addition besides an increased coverage. What is the purpose of testing this method ? None besides that Thread.sleep is working. It is obvious due to your question about the assertion that it is a test you dont want to write ! – Emerson Cod Jan 23 '16 at 21:26
  • I agree, but I am working on a project where we enforce 100% code coverage. – Paul Fournel Jan 23 '16 at 21:27
  • 2
    if this is a project obligation I would create a test without any assertion - you get your 100% and the test will not fail... or `AssertEquals(true, true)`. – Emerson Cod Jan 23 '16 at 21:32
  • Make sens. I will do something like this. – Paul Fournel Jan 23 '16 at 21:35

4 Answers4

6

You need to interrupt it from another thread. For example:

 Thread t = new Thread() {
     public void run () {
        SleeperMillis.sleepInMillis(new Duration(10000000l));
     }
 }.start();
 Thread.sleep(100); // let the other thread start
 t.interrupt;
Dima
  • 39,570
  • 6
  • 44
  • 70
  • Thx! this helps, but what would you assert in a test? – Paul Fournel Jan 23 '16 at 21:26
  • I don't know, it depends on what kind of behavior or contract you are trying to verify. What it is you want to test specifically? That jdk implements sleep() correctly? It is usually not a good idea to test the behavior of functions that you did not write – Dima Jan 23 '16 at 22:41
5

You don't need to actually interrupt the thread. You can use PowerMockito to mock the static method Thread.sleep()

@RunWith(PowerMockRunner.class)
@PrepareForTest(Thread.class)
public class TestClass {

    @Test
    public void testSleepInMillis() throws Exception {
        PowerMockito.mockStatic(Thread.class);
        PowerMockito.doThrow(new InterruptedException ()).when(Thread.class);

        try {
            SleepHelper.sleepInMillis(11);
            fail("expected exception");
        } catch (InterruptedException e) {
            System.out.println("all good");
        }

    }
Nir Levy
  • 12,750
  • 3
  • 21
  • 38
  • FTR this doesn't work in the new Mockito static mocking... org.mockito.exceptions.base.MockitoException: It is not possible to mock static methods of java.lang.Thread to avoid interfering with class loading what leads to infinite loops – JGleason May 21 '21 at 15:33
4

You don't test it, because you can't assert its results, and you can't assert it because Thread.sleep is not accurate or guaranteed to sleep for this duration of time, and the test results will differ from run to run.

Mocking is a better option here.

Btw, it is not just that your tests aren't predictable, your code that uses Thread.sleep in production is going to be unpredictable for the same reasons. Thread.sleep(some magic number goes here) usually indicates a badly written program.

Sleiman Jneidi
  • 22,907
  • 14
  • 56
  • 77
0

I wouldn't bother testing it. 100% coverage is excessive. However, you could do it like this:

@Test
public void testException() throws Exception {

    // Capture the system error stream, so that we can test that the expected exception is printed.
    ByteArrayOutputStream capturedErrors = new ByteArrayOutputStream();
    System.setErr(new PrintStream(capturedErrors));

    // Create a new thread on which to run the candidate method.
    Thread thread = new Thread() {
        @Override
        public void run() {
            SleepingHelper.sleepInMillis(Duration.ofMillis(10));
        }
    };

    // Start the second thread.
    thread.start();

    // Interrupt the second thread. (The candidate method hasn't finished yet. It takes 10 milliseconds to run.)
    thread.interrupt();

    // Wait for the thread to die (and write out the stack-trace).
    thread.join();

    // Test that the expected exception's stack trace was printed to the system error stream.
    // The output should start with the exception's name.
    String output = capturedErrors.toString();
    int lengthOfExceptionName = "java.lang.InterruptedException".length();
    assertEquals(output.substring(0, lengthOfExceptionName), "java.lang.InterruptedException");
}
whistling_marmot
  • 3,561
  • 3
  • 25
  • 39