CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-scalatest--scalatest-2-11

ScalaTest is a comprehensive testing framework for Scala and Java that provides multiple testing styles and sophisticated matcher libraries.

Pending
Overview
Eval results
Files

async-testing.mddocs/

Async Testing

ScalaTest provides comprehensive support for asynchronous testing with Future-based tests, automatic timeout handling, and integration with concurrent utilities. All test styles have async variants that return Future[Assertion] instead of Assertion.

Capabilities

Async Test Suites

All ScalaTest styles have async variants for testing asynchronous code.

import org.scalatest.funsuite.AsyncFunSuite
import org.scalatest.flatspec.AsyncFlatSpec
import org.scalatest.wordspec.AsyncWordSpec
import org.scalatest.freespec.AsyncFreeSpec
import org.scalatest.featurespec.AsyncFeatureSpec
import org.scalatest.funspec.AsyncFunSpec
import org.scalatest.propspec.AsyncPropSpec

// Base async test suite trait
trait AsyncTestSuite extends Suite with RecoverMethods with CompleteLastly {
  // Test methods return Future[Assertion] instead of Assertion
  protected def transformToOutcome(testFun: => Future[compatible.Assertion]): () => AsyncOutcome
  
  // Execution context for running async tests
  implicit def executionContext: ExecutionContext
  
  // Timeout configuration
  implicit val patienceConfig: PatienceConfig
}

// Async FunSuite example
abstract class AsyncFunSuite extends AsyncTestSuite with AsyncTestSuiteMixin {
  protected final class AsyncFunSuiteAsyncTest(testName: String, testTags: Tag*)
    extends AsyncTest(testName, testTags: _*)
  
  def test(testName: String, testTags: Tag*)(testFun: => Future[compatible.Assertion]): Unit
  def ignore(testName: String, testTags: Tag*)(testFun: => Future[compatible.Assertion]): Unit
}

Async Test Examples:

import org.scalatest.funsuite.AsyncFunSuite
import scala.concurrent.Future
import scala.concurrent.duration._

class AsyncCalculatorSuite extends AsyncFunSuite {
  test("async addition should work") {
    val futureResult = Future {
      Thread.sleep(100)  // Simulate async work
      2 + 2
    }
    
    futureResult.map { result =>
      assert(result === 4)
    }
  }
  
  test("multiple async operations") {
    val future1 = Future(10)
    val future2 = Future(20)
    
    for {
      a <- future1
      b <- future2
    } yield {
      assert(a + b === 30)
    }
  }
  
  test("async failure handling") {
    val failingFuture = Future.failed[Int](new RuntimeException("Expected failure"))
    
    recoverToSucceededIf[RuntimeException] {
      failingFuture
    }
  }
}

Future Assertions

Specialized assertion methods for Future-based testing.

import org.scalatest.concurrent.ScalaFutures

trait ScalaFutures {
  // Configuration for Future handling
  implicit val patienceConfig: PatienceConfig
  
  // Block until Future completes and apply function to result
  def whenReady[T](future: Future[T], timeout: Timeout = timeout, interval: Interval = interval)
                  (fun: T => Unit): Unit
  
  // Extract Future value (blocking)
  implicit class FutureValues[T](future: Future[T]) {
    def futureValue: T
    def futureValue(timeout: Timeout): T
  }
  
  // Assert Future completes successfully
  def noException should be thrownBy future
  
  // Assert Future fails with specific exception
  a [ExceptionType] should be thrownBy future
}

// Patience configuration
case class PatienceConfig(timeout: Span, interval: Span)
object PatienceConfig {
  implicit val defaultPatienceConfig: PatienceConfig = 
    PatienceConfig(timeout = scaled(Span(150, Millis)), interval = scaled(Span(15, Millis)))
}

Future Assertion Examples:

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import scala.concurrent.Future
import scala.concurrent.duration._

class FutureAssertionSpec extends AnyFlatSpec with Matchers with ScalaFutures {
  implicit override val patienceConfig = PatienceConfig(
    timeout = scaled(Span(5, Seconds)),
    interval = scaled(Span(50, Millis))
  )
  
  "Future assertions" should "work with whenReady" in {
    val future = Future { 42 }
    
    whenReady(future) { result =>
      result should be(42)
    }
  }
  
  "Future values" should "be extractable" in {
    val future = Future { "hello world" }
    
    future.futureValue should startWith("hello")
    future.futureValue should have length 11
  }
  
  "Failed futures" should "be testable" in {
    val failedFuture = Future.failed[String](new IllegalArgumentException("Bad argument"))
    
    a [IllegalArgumentException] should be thrownBy {
      failedFuture.futureValue
    }
  }
}

Async Recovery Methods

Handle and test for expected failures in async code.

trait RecoverMethods {
  // Recover from expected exception type
  def recoverToSucceededIf[T <: AnyRef](future: Future[Any])
                                       (implicit classTag: ClassTag[T]): Future[Assertion]
  
  // Recover and return the exception
  def recoverToExceptionIf[T <: AnyRef](future: Future[Any])
                                       (implicit classTag: ClassTag[T]): Future[T]
}

// Usage in async tests
class AsyncRecoverySpec extends AsyncFunSuite {
  test("should recover from expected exception") {
    val failingFuture = Future.failed[Int](new IllegalArgumentException("Expected"))
    
    recoverToSucceededIf[IllegalArgumentException] {
      failingFuture
    }
  }
  
  test("should capture and examine exception") {
    val failingFuture = Future.failed[Int](new IllegalArgumentException("Test message"))
    
    recoverToExceptionIf[IllegalArgumentException] {
      failingFuture
    }.map { exception =>
      assert(exception.getMessage === "Test message")
    }
  }
}

Async Fixture Support

Manage asynchronous test fixtures and resources.

import org.scalatest.funsuite.FixtureAsyncFunSuite

// Async fixture suite
abstract class FixtureAsyncFunSuite extends FixtureAsyncTestSuite with AsyncTestSuiteMixin {
  type FixtureParam
  
  // Async fixture management
  def withFixture(test: OneArgAsyncTest): FutureOutcome
  
  // Async test definition
  def test(testName: String, testTags: Tag*)(testFun: FixtureParam => Future[compatible.Assertion]): Unit
}

// Async fixture example
class AsyncDatabaseSuite extends FixtureAsyncFunSuite {
  type FixtureParam = Database
  
  override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
    val database = Database.connect()
    complete {
      super.withFixture(test.toNoArgAsyncTest(database))
    } lastly {
      database.close()
    }
  }
  
  test("async database operations") { db =>
    for {
      _ <- db.insert("user", Map("name" -> "Alice"))
      user <- db.findByName("Alice")
    } yield {
      assert(user.name === "Alice")
    }
  }
}

Eventually and Async Patience

Test conditions that should eventually become true.

import org.scalatest.concurrent.Eventually

trait Eventually {
  // Repeatedly test condition until it succeeds or times out
  def eventually[T](fun: => T)(implicit config: PatienceConfig): T
  
  // Configuration for eventually
  implicit val patienceConfig: PatienceConfig
}

// Integration with async tests
trait AsyncEventually extends Eventually {
  def eventually[T](fun: => Future[T])(implicit config: PatienceConfig): Future[T]
}

Eventually Examples:

import org.scalatest.concurrent.Eventually
import org.scalatest.time.{Millis, Seconds, Span}

class EventuallySpec extends AnyFlatSpec with Eventually {
  implicit override val patienceConfig = PatienceConfig(
    timeout = scaled(Span(5, Seconds)),
    interval = scaled(Span(100, Millis))
  )
  
  "Eventually" should "wait for condition to become true" in {
    var counter = 0
    
    eventually {
      counter += 1
      assert(counter >= 10)
    }
  }
  
  "Eventually with async" should "work with futures" in {
    @volatile var ready = false
    
    // Simulate async operation that sets ready flag
    Future {
      Thread.sleep(1000)
      ready = true
    }
    
    eventually {
      assert(ready === true)
    }
  }
}

Async Test Execution Context

Configure execution context for async tests.

// Default execution context
import org.scalatest.concurrent.ScalaFutures._

trait AsyncTestSuite {
  // Default execution context (can be overridden)
  implicit def executionContext: ExecutionContext = 
    scala.concurrent.ExecutionContext.Implicits.global
}

// Custom execution context
class CustomAsyncSuite extends AsyncFunSuite {
  // Use custom thread pool
  implicit override def executionContext: ExecutionContext = 
    ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))
}

Async Timeouts and Configuration

Configure timeouts and patience parameters for async operations.

import org.scalatest.time._

// Time span specifications
case class Span(length: Long, unit: Units)

// Timeout and interval configuration
case class PatienceConfig(timeout: Span, interval: Span) {
  def scaled(factor: Double): PatienceConfig
}

// Predefined time units
object Span {
  def apply(length: Long, unit: Units): Span
}

sealed abstract class Units
case object Nanosecond extends Units
case object Nanoseconds extends Units
case object Microsecond extends Units
case object Microseconds extends Units
case object Millisecond extends Units
case object Milliseconds extends Units
case object Millis extends Units
case object Second extends Units
case object Seconds extends Units
case object Minute extends Units
case object Minutes extends Units
case object Hour extends Units
case object Hours extends Units
case object Day extends Units
case object Days extends Units

Timeout Configuration Examples:

import org.scalatest.time._
import scala.concurrent.duration._

class TimeoutConfigSpec extends AsyncFunSuite {
  // Custom patience configuration
  implicit override val patienceConfig = PatienceConfig(
    timeout = scaled(Span(10, Seconds)),
    interval = scaled(Span(200, Millis))
  )
  
  test("long running operation") {
    val longFuture = Future {
      Thread.sleep(2000)  // 2 second delay
      "completed"
    }
    
    longFuture.map { result =>
      assert(result === "completed")
    }
  }
  
  test("operation with custom timeout") {
    val future = Future { "result" }
    
    whenReady(future, timeout(5.seconds)) { result =>
      assert(result === "result")
    }
  }
}

Async Parallel Testing

Run async tests in parallel for better performance.

// Parallel async execution
trait ParallelTestExecution { this: AsyncTestSuite =>
  // Enable parallel execution of async tests
}

class ParallelAsyncSuite extends AsyncFunSuite with ParallelTestExecution {
  test("parallel async test 1") {
    Future {
      Thread.sleep(100)
      assert(1 + 1 === 2)
    }
  }
  
  test("parallel async test 2") {
    Future {
      Thread.sleep(100)
      assert(2 + 2 === 4)
    }
  }
}

Integration with Reactive Streams

Test reactive streams and async data processing.

// Example with Akka Streams (not part of ScalaTest core)
import akka.stream.scaladsl.Source
import akka.stream.testkit.scaladsl.TestSink

class StreamTestSpec extends AsyncFunSuite {
  test("stream processing") {
    val source = Source(1 to 10)
      .map(_ * 2)
      .filter(_ > 10)
    
    val future = source.runFold(0)(_ + _)
    
    future.map { sum =>
      assert(sum === 60)  // 12 + 14 + 16 + 18 + 20 = 80
    }
  }
}

Async Error Handling Patterns

Common patterns for handling errors in async tests.

class AsyncErrorHandlingSpec extends AsyncFunSuite {
  test("transform failures") {
    val future = Future.failed[Int](new RuntimeException("Original error"))
    
    future.recover {
      case _: RuntimeException => 42
    }.map { result =>
      assert(result === 42)
    }
  }
  
  test("chain operations with error handling") {
    def mightFail(value: Int): Future[Int] = {
      if (value < 0) Future.failed(new IllegalArgumentException("Negative value"))
      else Future.successful(value * 2)
    }
    
    val result = for {
      a <- Future.successful(5)
      b <- mightFail(a)
      c <- mightFail(b)
    } yield c
    
    result.map { finalValue =>
      assert(finalValue === 20)  // 5 * 2 * 2
    }
  }
  
  test("verify specific failure") {
    val future = Future.failed[String](new IllegalStateException("Bad state"))
    
    recoverToSucceededIf[IllegalStateException] {
      future
    }
  }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-scalatest--scalatest-2-11

docs

assertions-matchers.md

async-testing.md

index.md

scalactic-utilities.md

test-execution.md

test-styles.md

tile.json