Below is a simplified code to understand how eventually works. But it only attempts assertion once, and eventually doesn't attempts second assertion.
package whisk_main
import org.scalatest.concurrent.Eventually.eventually
import org.scalatest.concurrent.Waiters.{interval, timeout}
import org.scalatest.flatspec.AsyncFlatSpecLike
import org.scalatest.matchers.should.Matchers
import org.scalatest.time.{Millis, Seconds, Span}
class EventuallySpec extends AsyncFlatSpecLike with Matchers{
"eventually" should "work, as expected" in {
val runnable = new Runnable {
override def run(): Unit = {
val currentThread = Thread.currentThread()
println(s"$currentThread starts here")
Thread.sleep(5000)
println(s"$currentThread ends here")
}
}
val threadThatSleepsForFiveSeconds = new Thread(runnable)
threadThatSleepsForFiveSeconds.start()
eventually(timeout(Span(15, Seconds)), interval(Span(2, Millis))) {
println("asserting thread state")
threadThatSleepsForFiveSeconds.getState should be(Thread.State.TERMINATED)
}
}
}
Below is the exception trace :-
Thread[Thread-1,5,main] starts here
asserting thread state
TIMED_WAITING was not equal to TERMINATED
ScalaTestFailureLocation: whisk_main.EventuallySpec at (EventuallySpec.scala:28)
Expected :TERMINATED
Actual :TIMED_WAITING
<Click to see difference>
org.scalatest.exceptions.TestFailedException: TIMED_WAITING was not equal to TERMINATED
at org.scalatest.matchers.MatchersHelper$.indicateFailure(MatchersHelper.scala:344)
at org.scalatest.matchers.should.Matchers$ShouldMethodHelperClass.shouldMatcher(Matchers.scala:6778)
at org.scalatest.matchers.should.Matchers$AnyShouldWrapper.should(Matchers.scala:6822)
at whisk_main.EventuallySpec.$anonfun$new$2(EventuallySpec.scala:28)
I tried below example also that is documented on http://doc.scalatest.org/1.8/org/scalatest/concurrent/Eventually.html :-
val xs = 1 to 125
val it = xs.iterator
eventually { it.next should be (3) }
Above example also fails with below example :-
1 was not equal to 3
ScalaTestFailureLocation: whisk_main.EventuallyExample at (EventuallyExample.scala:12)
Expected :3
Actual :1
<Click to see difference>
org.scalatest.exceptions.TestFailedException: 1 was not equal to 3
at org.scalatest.matchers.MatchersHelper$.indicateFailure(MatchersHelper.scala:344)
at org.scalatest.matchers.should.Matchers$ShouldMethodHelperClass.shouldMatcher(Matchers.scala:6778)
at org.scalatest.matchers.should.Matchers$AnyShouldWrapper.should(Matchers.scala:6822)
at whisk_main.EventuallyExample.$anonfun$new$2(EventuallyExample.scala:12)
Kindly suggest, why eventually is not working till it succeeds.
Your issue is that you are extending AsyncFlatSpecLike
while your tests are synchronous. The behaviour you observe, is that AsyncFlatSpecLike
takes the first Assertion of each of your tests, and fails upon it. Changing the class declaration into:
class EventuallySpec extends AnyFlatSpecLike with Matchers {
will keep the tests synchronous, and make your tests pass, with the following output:
Thread[Thread-0,5,main] starts here
asserting thread state
asserting thread state
.
.
.
asserting thread state
asserting thread state
Thread[Thread-0,5,main] ends here
asserting thread state
as you'd expect.
Another option you have, is using AsyncFlatSpecLike
if your test is really async. For example the following test passes as well:
class MySpec extends AsyncFlatSpecLike with Matchers {
"eventually" should "work, as expected" in {
val runnable = new Runnable {
override def run(): Unit = {
val currentThread = Thread.currentThread()
println(s"$currentThread starts here")
Thread.sleep(5000)
println(s"$currentThread ends here")
}
}
val threadThatSleepsForFiveSeconds = new Thread(runnable)
threadThatSleepsForFiveSeconds.start()
eventually(timeout(Span(15, Seconds)), interval(Span(2, Millis))) {
println("asserting thread state")
Future {
Thread.sleep(10)
threadThatSleepsForFiveSeconds.getState must be(Thread.State.TERMINATED)
}
}
}
}