org.scalatest.concurrent

Eventually

trait Eventually extends PatienceConfiguration

Trait that provides the eventually construct, which periodically retries executing a passed by-name parameter, until it either succeeds or the configured timeout has been surpassed.

The by-name parameter "succeeds" if it returns a result. It "fails" if it throws any exception that would normally cause a test to fail. (These are any exceptions except TestPendingException and Errors listed in the Treatment of java.lang.Errors section of the documentation of trait Suite.)

For example, the following invocation of eventually would succeed (not throw an exception):

val xs = 1 to 125
val it = xs.iterator
eventually { it.next should be (3) }

However, because the default timeout is 150 milliseconds, the following invocation of eventually would ultimately produce a TestFailedDueToTimeoutException:

val xs = 1 to 125
val it = xs.iterator
eventually { Thread.sleep(50); it.next should be (110) }

Assuming the default configuration parameters, a timeout of 150 milliseconds and an interval of 15 milliseconds, were passed implicitly to eventually, the detail message of the thrown TestFailedDueToTimeoutException would look like:

The code passed to eventually never returned normally. Attempted 2 times over 166.682 milliseconds. Last failure message: 2 was not equal to 110.

The cause of the thrown TestFailedDueToTimeoutException will be the exception most recently thrown by the block of code passed to eventually. (In the previous example, the cause would be the TestFailedException with the detail message 2 was not equal to 100.)

Configuration of eventually

The eventually methods of this trait can be flexibly configured. The two configuration parameters for eventually along with their default values and meanings are described in the following table:

Configuration Parameter Default Value Meaning
timeout scaled(150 milliseconds) the maximum amount of time to allow unsuccessful attempts before giving up and throwing TestFailedDueToTimeoutException
interval scaled(15 milliseconds) the amount of time to sleep between each attempt

The default values of both timeout and interval are passed to the scaled method, inherited from ScaledTimeSpans, so that the defaults can be scaled up or down together with other scaled time spans. See the documentation for trait ScaledTimeSpans for more information.

The eventually methods of trait Eventually each take a PatienceConfig object as an implicit parameter. This object provides values for the two configuration parameters. (These configuration parameters are called "patience" because they determine how patient tests will be with asynchronous operations: how long they will tolerate failures before giving up and how long they will wait before checking again after a failure.) Trait Eventually provides an implicit val named patienceConfig with each configuration parameter set to its default value. If you want to set one or more configuration parameters to a different value for all invocations of eventually in a suite you can override this val (or hide it, for example, if you are importing the members of the Eventually companion object rather than mixing in the trait). For example, if you always want the default timeout to be 2 seconds and the default interval to be 5 milliseconds, you can override patienceConfig, like this:

implicit override val patienceConfig =
  PatienceConfig(timeout = scaled(Span(2, Seconds)), interval = scaled(Span(5, Millis)))

Or, hide it by declaring a variable of the same name in whatever scope you want the changed values to be in effect:

implicit val patienceConfig =
  PatienceConfig(timeout = scaled(Span(2, Seconds)), interval = scaled(Span(5, Millis)))

Passing your new default values to scaled is optional, but a good idea because it allows them to be easily scaled if run on a slower or faster system.

In addition to taking a PatienceConfig object as an implicit parameter, the eventually methods of trait Eventually include overloaded forms that take one or two PatienceConfigParam objects that you can use to override the values provided by the implicit PatienceConfig for a single eventually invocation. For example, if you want to set timeout to 5 seconds for just one particular eventually invocation, you can do so like this:

eventually (timeout(Span(5, Seconds))) { Thread.sleep(10); it.next should be (110) }

This invocation of eventually will use 5 seconds for the timeout and whatever value is specified by the implicitly passed PatienceConfig object for the interval configuration parameter. If you want to set both configuration parameters in this way, just list them separated by commas:

eventually (timeout(Span(5, Seconds)), interval(Span(5, Millis))) { it.next should be (110) }

You can also import or mix in the members of SpanSugar if you want a more concise DSL for expressing time spans:

eventually (timeout(5 seconds), interval(5 millis)) { it.next should be (110) }

Note that ScalaTest will not scale any time span that is not explicitly passed to scaled to make the meaning of the code as obvious as possible. Thus if you ask for "timeout(5 seconds)" you will get exactly that: a timeout of five seconds. If you want such explicitly given values to be scaled, you must pass them to scale explicitly like this:

eventually (timeout(scaled(5 seconds))) { it.next should be (110) }

The previous code says more clearly that the timeout will be five seconds, unless scaled higher or lower by the scaled method.

Simple backoff algorithm

The eventually methods employ a very simple backoff algorithm to try and maximize the speed of tests. If an asynchronous operation completes quickly, a smaller interval will yield a faster test. But if an asynchronous operation takes a while, a small interval will keep the CPU busy repeatedly checking and rechecking a not-ready operation, to some extent taking CPU cycles away from other processes that could proceed. To strike the right balance between these design tradeoffs, the eventually methods will check more frequently during the initial interval.

Rather than sleeping an entire interval if the initial attempt fails, eventually will only sleep 1/10 of the configured interval. It will continue sleeping only 1/10 of the configured interval until the configured interval has passed, after which it sleeps the configured interval between attempts. Here's an example in which the timeout is set equal to the interval:

val xs = 1 to 125
val it = xs.iterator
eventually(timeout(100 milliseconds), interval(100 milliseconds)) { it.next should be (110) }

Even though this call to eventually will time out after only one interval, approximately, the error message will likely report that more than one (and less than ten) attempts were made:

The code passed to eventually never returned normally. Attempted 6 times over 100.485 milliseconds. Last failure message: 6 was not equal to 110.

Note that if the initial attempt takes longer than the configured interval to complete, eventually will never sleep for a 1/10 interval. You can observe this behavior in the second example above in which the first statement in the block of code passed to eventually was Thread.sleep(50).

Usage note: Eventually intended primarily for integration testing

Although the default timeouts of trait Eventually are tuned for unit testing, the use of Eventually in unit tests is a choice you should question. Usually during unit testing you'll want to mock out subsystems that would require Eventually, such as network services with varying and unpredictable response times. This will allow your unit tests to run as fast as possible while still testing the focused bits of behavior they are designed to test.

Nevertheless, because sometimes it will make sense to use Eventually in unit tests (and because it is destined to happen anyway even when it isn't the best choice), Eventually by default uses timeouts tuned for unit tests: Calls to eventually are more likely to succeed on fast development machines, and if a call does time out, it will do so quickly so the unit tests can move on.

When you are using Eventually for integration testing, therefore, the default timeout and interval may be too small. A good way to override them is by mixing in trait IntegrationPatience or a similar trait of your own making. Here's an example:

class ExampleSpec extends FeatureSpec with Eventually with IntegrationPatience {
  // Your integration tests here...
}

Trait IntegrationPatience increases the default timeout from 150 milliseconds to 15 seconds, the default interval from 15 milliseconds to 150 milliseconds. If need be, you can do fine tuning of the timeout and interval by specifying a time span scale factor when you run your tests.

Linear Supertypes
Known Subclasses
Ordering
  1. Alphabetic
  2. By inheritance
Inherited
  1. Eventually
  2. PatienceConfiguration
  3. AbstractPatienceConfiguration
  4. ScaledTimeSpans
  5. AnyRef
  6. Any
  1. Hide All
  2. Show all
Learn more about member selection
Visibility
  1. Public
  2. All

Type Members

  1. final case class PatienceConfig(timeout: Span = ..., interval: Span = ...) extends Product with Serializable

    Configuration object for asynchronous constructs, such as those provided by traits Eventually and AsyncAssertions.

Value Members

  1. final def !=(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  2. final def !=(arg0: Any): Boolean

    Definition Classes
    Any
  3. final def ##(): Int

    Definition Classes
    AnyRef → Any
  4. final def ==(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  5. final def ==(arg0: Any): Boolean

    Definition Classes
    Any
  6. final def asInstanceOf[T0]: T0

    Definition Classes
    Any
  7. def clone(): AnyRef

    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  8. final def eq(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  9. def equals(arg0: Any): Boolean

    Definition Classes
    AnyRef → Any
  10. def eventually[T](fun: ⇒ T)(implicit config: PatienceConfig): T

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    The by-name parameter "succeeds" if it returns a result. It "fails" if it throws any exception that would normally cause a test to fail. (These are any exceptions except TestPendingException and Errors listed in the Treatment of java.lang.Errors section of the documentation of trait Suite.)

    The maximum amount of time in milliseconds to tolerate unsuccessful attempts before giving up is configured by the timeout field of the PatienceConfig passed implicitly as the last parameter. The interval to sleep between attempts is configured by the interval field of the PatienceConfig passed implicitly as the last parameter.

    fun

    the by-name parameter to repeatedly invoke

    config

    the PatienceConfig object containing the timeout and interval parameters

    returns

    the result of invoking the fun by-name parameter, the first time it succeeds

  11. def eventually[T](interval: Interval)(fun: ⇒ T)(implicit config: PatienceConfig): T

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    The by-name parameter "succeeds" if it returns a result. It "fails" if it throws any exception that would normally cause a test to fail. (These are any exceptions except TestPendingException and Errors listed in the Treatment of java.lang.Errors section of the documentation of trait Suite.)

    The maximum amount of time in milliseconds to tolerate unsuccessful attempts before giving up is configured by the timeout field of the PatienceConfig passed implicitly as the last parameter. The interval to sleep between attempts is configured by the value contained in the passed interval parameter.

    interval

    the Interval configuration parameter

    fun

    the by-name parameter to repeatedly invoke

    config

    the PatienceConfig object containing the (used) timeout and (unused) interval parameters

    returns

    the result of invoking the fun by-name parameter, the first time it succeeds

  12. def eventually[T](timeout: Timeout)(fun: ⇒ T)(implicit config: PatienceConfig): T

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    The by-name parameter "succeeds" if it returns a result. It "fails" if it throws any exception that would normally cause a test to fail. (These are any exceptions except TestPendingException and Errors listed in the Treatment of java.lang.Errors section of the documentation of trait Suite.)

    The maximum amount of time in milliseconds to tolerate unsuccessful attempts before giving up and throwing TestFailedException is configured by the value contained in the passed timeout parameter. The interval to sleep between attempts is configured by the interval field of the PatienceConfig passed implicitly as the last parameter.

    timeout

    the Timeout configuration parameter

    fun

    the by-name parameter to repeatedly invoke

    config

    the PatienceConfig object containing the (unused) timeout and (used) interval parameters

    returns

    the result of invoking the fun by-name parameter, the first time it succeeds

  13. def eventually[T](timeout: Timeout, interval: Interval)(fun: ⇒ T): T

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    Invokes the passed by-name parameter repeatedly until it either succeeds, or a configured maximum amount of time has passed, sleeping a configured interval between attempts.

    The by-name parameter "succeeds" if it returns a result. It "fails" if it throws any exception that would normally cause a test to fail. (These are any exceptions except TestPendingException and Errors listed in the Treatment of java.lang.Errors section of the documentation of trait Suite.)

    The maximum amount of time in milliseconds to tolerate unsuccessful attempts before giving up and throwing TestFailedException is configured by the value contained in the passed timeout parameter. The interval to sleep between attempts is configured by the value contained in the passed interval parameter.

    timeout

    the Timeout configuration parameter

    interval

    the Interval configuration parameter

    fun

    the by-name parameter to repeatedly invoke

    returns

    the result of invoking the fun by-name parameter, the first time it succeeds

  14. def finalize(): Unit

    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( classOf[java.lang.Throwable] )
  15. final def getClass(): Class[_]

    Definition Classes
    AnyRef → Any
  16. def hashCode(): Int

    Definition Classes
    AnyRef → Any
  17. def interval(value: Span): Interval

    Returns an Interval configuration parameter containing the passed value, which specifies the amount of time to sleep after a retry.

    Returns an Interval configuration parameter containing the passed value, which specifies the amount of time to sleep after a retry.

    Definition Classes
    PatienceConfiguration
  18. final def isInstanceOf[T0]: Boolean

    Definition Classes
    Any
  19. final def ne(arg0: AnyRef): Boolean

    Definition Classes
    AnyRef
  20. final def notify(): Unit

    Definition Classes
    AnyRef
  21. final def notifyAll(): Unit

    Definition Classes
    AnyRef
  22. implicit def patienceConfig: PatienceConfig

    Implicit PatienceConfig value providing default configuration values.

    Implicit PatienceConfig value providing default configuration values.

    To change the default configuration, override or hide this def with another implicit PatienceConfig containing your desired default configuration values.

    Definition Classes
    PatienceConfigurationAbstractPatienceConfiguration
  23. final def scaled(span: Span): Span

    Scales the passed Span by the Double factor returned by spanScaleFactor.

    Scales the passed Span by the Double factor returned by spanScaleFactor.

    The Span is scaled by invoking its scaledBy method, thus this method has the same behavior: The value returned by spanScaleFactor can be any positive number or zero, including a fractional number. A number greater than one will scale the Span up to a larger value. A fractional number will scale it down to a smaller value. A factor of 1.0 will cause the exact same Span to be returned. A factor of zero will cause Span.ZeroLength to be returned. If overflow occurs, Span.Max will be returned. If underflow occurs, Span.ZeroLength will be returned.

    Definition Classes
    ScaledTimeSpans
    Exceptions thrown
    IllegalArgumentException

    if the value returned from spanScaleFactor is less than zero

  24. def spanScaleFactor: Double

    The factor by which the scaled method will scale Spans.

    The factor by which the scaled method will scale Spans.

    The default implementation of this method will return the span scale factor that was specified for the run, or 1.0 if no factor was specified. For example, you can specify a span scale factor when invoking ScalaTest via the command line by passing a -F argument to Runner.

    Definition Classes
    ScaledTimeSpans
  25. final def synchronized[T0](arg0: ⇒ T0): T0

    Definition Classes
    AnyRef
  26. def timeout(value: Span): Timeout

    Returns a Timeout configuration parameter containing the passed value, which specifies the maximum amount to wait for an asynchronous operation to complete.

    Returns a Timeout configuration parameter containing the passed value, which specifies the maximum amount to wait for an asynchronous operation to complete.

    Definition Classes
    PatienceConfiguration
  27. def toString(): String

    Definition Classes
    AnyRef → Any
  28. final def wait(): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  29. final def wait(arg0: Long, arg1: Int): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  30. final def wait(arg0: Long): Unit

    Definition Classes
    AnyRef
    Annotations
    @throws( ... )

Inherited from PatienceConfiguration

Inherited from ScaledTimeSpans

Inherited from AnyRef

Inherited from Any

Ungrouped