or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

actor-refs.mdconfiguration.mdcore-testing.mddispatchers.mdevent-filtering.mdindex.mdjava-dsl.mdpackage-functions.mdsynchronization.mdtest-actors.mdutilities.md
tile.json

configuration.mddocs/

Configuration and Extensions

TestKit configuration system and Akka extensions for customizing test behavior, timeouts, and environment settings.

Capabilities

TestKitExtension

Akka extension for accessing TestKit settings and configuration.

object TestKitExtension extends ExtensionId[TestKitSettings] with ExtensionIdProvider {
  def apply(system: ActorSystem): TestKitSettings
  def apply(system: ExtendedActorSystem): TestKitSettings  
  def createExtension(system: ExtendedActorSystem): TestKitSettings
  def lookup(): ExtensionId[_ <: Extension] = TestKitExtension
}

TestKitSettings

Configuration settings class for TestKit behavior and timeouts.

class TestKitSettings(val config: Config) extends Extension {
  import TestKitSettings._
  
  // Core timing settings
  val TestTimeFactor: Double
  val SingleExpectDefaultTimeout: FiniteDuration
  val MultiExpectDefaultTimeout: FiniteDuration  
  val ExpectNoMessageDefaultTimeout: FiniteDuration
  val TestEventFilterLeeway: FiniteDuration
  val DefaultTimeout: Timeout
  
  // Utility methods
  def dilated(duration: FiniteDuration): FiniteDuration
}

object TestKitSettings {
  def apply(system: ActorSystem): TestKitSettings = TestKitExtension(system)
}

Configuration Reference

Default Configuration Values

akka.test {
  # Time factor for dilating timeouts in tests
  # Set higher for slow test environments (CI, etc.)
  timefactor = 1.0
  
  # Default timeout for single message expectations  
  single-expect-default = 3s
  
  # Default timeout for multi-message expectations
  multi-expect-default = 3s
  
  # Default timeout for expectNoMessage calls
  expect-no-message-default = 100ms
  
  # Extra time to wait for expected log events in EventFilter
  filter-leeway = 3s
  
  # Default dispatcher for test actors
  default-timeout = 5s
}

Usage Examples

Accessing Configuration:

import akka.testkit.{TestKit, TestKitExtension}

class ConfigurationTest extends TestKit(ActorSystem("TestSystem")) {
  val settings = TestKitExtension(system)
  
  "TestKit configuration" should {
    "provide access to timing settings" in {
      settings.TestTimeFactor should be >= 1.0
      settings.SingleExpectDefaultTimeout should be > Duration.Zero
      settings.MultiExpectDefaultTimeout should be > Duration.Zero
      settings.ExpectNoMessageDefaultTimeout should be > Duration.Zero
      settings.TestEventFilterLeeway should be > Duration.Zero
      settings.DefaultTimeout.duration should be > Duration.Zero
    }
    
    "dilate durations correctly" in {
      val original = 1.second
      val dilated = settings.dilated(original)
      
      dilated should be >= original
      dilated should equal(Duration.fromNanos((original.toNanos * settings.TestTimeFactor).toLong))
    }
    
    "be accessible through extension" in {
      val settingsViaExtension = TestKitExtension(system)
      val settingsViaApply = TestKitSettings(system)
      
      settingsViaExtension should be theSameInstanceAs settingsViaApply
    }
  }
}

Custom Configuration

Override Default Values:

// Custom configuration in application.conf or test configuration
val customConfig = ConfigFactory.parseString("""
  akka.test {
    timefactor = 2.0
    single-expect-default = 5s
    multi-expect-default = 10s
    expect-no-message-default = 200ms
    filter-leeway = 5s
    default-timeout = 30s
  }
""")

val system = ActorSystem("CustomTestSystem", customConfig)

class CustomConfigTest extends TestKit(system) {
  "Custom configuration" should {
    "use overridden values" in {
      val settings = TestKitExtension(system)
      
      settings.TestTimeFactor should equal(2.0)
      settings.SingleExpectDefaultTimeout should equal(5.seconds)
      settings.MultiExpectDefaultTimeout should equal(10.seconds)
      settings.ExpectNoMessageDefaultTimeout should equal(200.millis)
      settings.TestEventFilterLeeway should equal(5.seconds)
      settings.DefaultTimeout.duration should equal(30.seconds)
    }
  }
}

Environment-Specific Configuration:

// Different configurations for different environments
object TestConfigurations {
  val ciConfig = ConfigFactory.parseString("""
    akka.test {
      timefactor = 3.0        # Slower CI environment
      single-expect-default = 10s
      filter-leeway = 10s
    }
  """)
  
  val localConfig = ConfigFactory.parseString("""
    akka.test {
      timefactor = 1.0        # Fast local development
      single-expect-default = 3s
      filter-leeway = 3s
    }
  """)
  
  val loadTestConfig = ConfigFactory.parseString("""
    akka.test {
      timefactor = 5.0        # Very slow load testing environment
      single-expect-default = 30s
      multi-expect-default = 60s
      filter-leeway = 15s
    }
  """)
  
  def getConfig(environment: String): Config = environment match {
    case "ci" => ciConfig
    case "local" => localConfig  
    case "load" => loadTestConfig
    case _ => ConfigFactory.empty()
  }
}

// Usage in tests
class EnvironmentConfigTest {
  val environment = sys.env.getOrElse("TEST_ENV", "local")
  val config = TestConfigurations.getConfig(environment)
  val system = ActorSystem("EnvTestSystem", config)
  
  // Tests will use environment-appropriate timeouts
}

Integration with TestKit Features

Automatic Timeout Usage:

class TimeoutIntegrationTest extends TestKit(ActorSystem("TestSystem")) with DefaultTimeout {
  "TestKit with configuration" should {
    "use configured timeouts automatically" in {
      val actor = system.actorOf(Props(new Actor {
        def receive = {
          case msg => 
            Thread.sleep(100) // Simulate work
            sender() ! s"processed: $msg"
        }
      }))
      
      // Uses SingleExpectDefaultTimeout from configuration
      actor ! "test"
      expectMsg("processed: test") // No explicit timeout needed
      
      // Uses ExpectNoMessageDefaultTimeout from configuration  
      expectNoMessage() // Uses configured default
    }
    
    "respect time dilation" in {
      import akka.testkit._
      
      // All dilated timeouts use TestTimeFactor
      within(2.seconds.dilated) {
        // Code that might run slowly in some environments
        Thread.sleep(100)
        expectNoMessage(100.millis.dilated)
      }
    }
  }
}

Event Filter Configuration Integration:

class EventFilterConfigTest extends TestKit(ActorSystem("TestSystem")) {
  "Event filters with configuration" should {
    "use configured filter leeway" in {
      val settings = TestKitExtension(system)
      
      // EventFilter internally uses TestEventFilterLeeway for timeout
      EventFilter.info(message = "test message").intercept {
        // Simulate slow logging
        Future {
          Thread.sleep(settings.TestEventFilterLeeway.toMillis / 2)
          log.info("test message")
        }
        
        // Filter waits up to filter-leeway time for the event
      }
    }
  }
}

Advanced Configuration Patterns

Dynamic Configuration:

class DynamicConfigTest {
  def createSystemWithTimeoutFactor(factor: Double): ActorSystem = {
    val config = ConfigFactory.parseString(s"""
      akka.test.timefactor = $factor
    """)
    ActorSystem("DynamicTestSystem", config)
  }
  
  "Dynamic configuration" should {
    "allow runtime configuration changes" in {
      val fastSystem = createSystemWithTimeoutFactor(1.0)
      val slowSystem = createSystemWithTimeoutFactor(5.0)
      
      val fastKit = new TestKit(fastSystem)
      val slowKit = new TestKit(slowSystem)
      
      import fastKit._
      val fastSettings = TestKitExtension(fastSystem)
      fastSettings.TestTimeFactor should equal(1.0)
      
      import slowKit._
      val slowSettings = TestKitExtension(slowSystem)
      slowSettings.TestTimeFactor should equal(5.0)
      
      fastSystem.terminate()
      slowSystem.terminate()
    }
  }
}

Configuration Validation:

class ConfigValidationTest extends TestKit(ActorSystem("TestSystem")) {
  "Configuration validation" should {
    "ensure reasonable timeout values" in {
      val settings = TestKitExtension(system)
      
      // Validate timeout values are reasonable
      settings.SingleExpectDefaultTimeout should be >= 100.millis
      settings.MultiExpectDefaultTimeout should be >= settings.SingleExpectDefaultTimeout
      settings.TestEventFilterLeeway should be >= 1.second
      settings.TestTimeFactor should be >= 1.0
      
      // Validate dilated timeouts don't exceed reasonable bounds
      val dilated = settings.dilated(1.minute)
      dilated should be <= 10.minutes // Reasonable upper bound
    }
    
    "provide consistent configuration access" in {
      // Multiple ways to access should return same instance
      val settings1 = TestKitExtension(system)
      val settings2 = TestKitSettings(system)
      
      settings1 should be theSameInstanceAs settings2
      settings1.TestTimeFactor should equal(settings2.TestTimeFactor)
    }
  }
}

Best Practices

  1. Environment-Specific Configuration: Use different time factors for different environments
  2. Reasonable Defaults: Start with default configuration and adjust based on test performance
  3. Consistent Access: Use TestKitExtension for consistent settings access
  4. Time Dilation: Always use dilated timeouts for environment independence
  5. Configuration Testing: Validate your configuration settings in tests
// Good: Environment-aware configuration
val timeFactor = if (sys.env.contains("CI")) 3.0 else 1.0
val config = ConfigFactory.parseString(s"akka.test.timefactor = $timeFactor")

// Good: Consistent settings access
val settings = TestKitExtension(system)
val timeout = settings.SingleExpectDefaultTimeout

// Good: Use dilated timeouts everywhere
expectMsg(5.seconds.dilated, expectedMessage)
within(10.seconds.dilated) { /* test code */ }

// Good: Validate configuration in tests
assert(settings.TestTimeFactor >= 1.0, "Time factor should be at least 1.0")
assert(settings.DefaultTimeout.duration > Duration.Zero, "Default timeout must be positive")

Common Configuration Issues

Performance Issues:

// Problem: Time factor too high for local development
akka.test.timefactor = 10.0  // Makes tests unnecessarily slow locally

// Solution: Environment-specific factors
akka.test.timefactor = ${?TEST_TIME_FACTOR}  // Override via environment variable

Timeout Issues:

// Problem: Timeouts too short for slow operations
akka.test.single-expect-default = 100ms  // Too short for most operations

// Solution: Reasonable defaults with dilation
akka.test.single-expect-default = 3s
// Then use .dilated in tests for environment adaptation

Filter Issues:

// Problem: Filter leeway too short for async logging
akka.test.filter-leeway = 100ms  // May miss async log events

// Solution: Adequate leeway for async operations
akka.test.filter-leeway = 3s