6

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

Piero Nadello
  • 69
  • 1
  • 1
  • 5
  • 1
    why you want to do that first of all? – virendrao Feb 24 '15 at 12:10
  • 2
    You'll hardly find a better approach, if you really want to test this particular requirement. Possibly you may let the timeout be configurable and use the minimum time for testing. – Marko Topolnik Feb 24 '15 at 12:15
  • As is hinted above, one question is why does your other object behave like that? But putting that aside, this approach seems fine, although it's quite coarsely grained; 1000ms is significantly longer than 500ms, – Duncan Jones Feb 24 '15 at 12:16
  • 1
    What if I remove the implicit dependency with the system clock and replace with a "stub-able" clock and elapse the time instantly? – Piero Nadello Apr 06 '16 at 09:28

2 Answers2

9

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.

Thirler
  • 20,239
  • 14
  • 63
  • 92
1

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.

Zielu
  • 8,312
  • 4
  • 28
  • 41
  • Given the current mocking frameworks, this seems like overengineering. Simply providing a protected method would be enough to mock it out for testing. – Marko Topolnik Feb 24 '15 at 12:26
  • It is personal taste, but I dont like partial mocking, as mocking methods of object being tested feels wrong and suggest that such logic could be delegated somewhere else == be overengineered. – Zielu Feb 24 '15 at 12:44
  • I think testing should be approached from a pragmatic standpoint: give the testing concern a share of the complexity budget, but do keep it at a bare minimum. So if there is opportunity to mock out a strategy method inside the tested object, by all means grab it. Keep in mind that complexity itself gives rise to maintenance and correctness issues, the very thing the tests are there to protect you from. – Marko Topolnik Feb 24 '15 at 12:50