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

package-functions.mddocs/

Package-Level Functions

Global utility functions and implicit classes available at the package level for common testing operations and time dilation.

Capabilities

Event Filtering Functions

Package-level functions for convenient event filtering operations.

package object testkit {
  // Event filtering with multiple filters
  def filterEvents[T](eventFilters: Iterable[EventFilter])(block: => T)(implicit system: ActorSystem): T
  def filterEvents[T](eventFilters: EventFilter*)(block: => T)(implicit system: ActorSystem): T
  
  // Exception filtering shorthand
  def filterException[T <: Throwable](block: => Unit)(implicit system: ActorSystem, t: ClassTag[T]): Unit
}

Usage Examples:

import akka.testkit._

class PackageFunctionsTest extends TestKit(ActorSystem("TestSystem")) {
  "Package-level filterEvents" should {
    "filter multiple event types" in {
      filterEvents(
        EventFilter.error(occurrences = 1),
        EventFilter.warning(pattern = "deprecated".r, occurrences = 2),
        EventFilter[RuntimeException](occurrences = 1)
      ) {
        val actor = system.actorOf(Props(new Actor {
          def receive = {
            case "error" => log.error("Test error")
            case "warn" => 
              log.warning("Use of deprecated feature")
              log.warning("Another deprecated warning")
            case "exception" => throw new RuntimeException("Test exception")
          }
        }))
        
        actor ! "error"
        actor ! "warn"
        actor ! "exception"
      }
    }
    
    "work with iterable filters" in {
      val filters = List(
        EventFilter.info(message = "Starting", occurrences = 1),
        EventFilter.info(message = "Completed", occurrences = 1)
      )
      
      filterEvents(filters) {
        val actor = system.actorOf(Props(new Actor {
          def receive = {
            case "process" =>
              log.info("Starting")
              // Do work
              log.info("Completed")
          }
        }))
        
        actor ! "process" 
      }
    }
  }
  
  "Package-level filterException" should {
    "provide shorthand for exception filtering" in {
      filterException[IllegalArgumentException] {
        val actor = system.actorOf(Props(new Actor {
          def receive = {
            case invalid: String if invalid.isEmpty => 
              throw new IllegalArgumentException("Empty string not allowed")
          }
        }))
        
        actor ! ""
      }
    }
    
    "work with type inference" in {
      import scala.reflect.ClassTag
      
      def testException[E <: Throwable : ClassTag](triggerException: => Unit): Unit = {
        filterException[E] {
          triggerException
        }
      }
      
      testException[NullPointerException] {
        throw new NullPointerException("Test NPE")
      }
    }
  }
}

Time Dilation Implicit Class

Implicit class for scaling durations based on test environment.

implicit class TestDuration(val duration: FiniteDuration) extends AnyVal {
  def dilated(implicit system: ActorSystem): FiniteDuration
}

Usage Examples:

import akka.testkit._
import scala.concurrent.duration._

class TimeDilationTest extends TestKit(ActorSystem("TestSystem")) {
  "TestDuration implicit class" should {
    "dilate durations for test reliability" in {
      val originalTimeout = 1.second
      val dilatedTimeout = originalTimeout.dilated
      
      // Dilated timeout accounts for slow test environments
      dilatedTimeout should be >= originalTimeout
      
      // Use in expectations
      within(5.seconds.dilated) {
        // Code that might run slowly in CI
        Thread.sleep(100)
        expectNoMessage(500.millis.dilated)
      }
    }
    
    "work with various duration types" in {
      // All duration types work
      val millis = 100.millis.dilated
      val seconds = 2.seconds.dilated  
      val minutes = 1.minute.dilated
      
      millis should be >= 100.millis
      seconds should be >= 2.seconds
      minutes should be >= 1.minute
    }
    
    "respect test time factor configuration" in {
      // Time factor configured in application.conf: akka.test.timefactor = 2.0
      val original = 1.second
      val dilated = original.dilated
      
      // With timefactor = 2.0, dilated should be ~2x original
      val settings = TestKitExtension(system)
      val expectedDilated = Duration.fromNanos((original.toNanos * settings.TestTimeFactor).toLong)
      
      dilated should be(expectedDilated)
    }
    
    "be used in actor timing tests" in {
      class TimedActor extends Actor {
        import context.dispatcher
        
        def receive = {
          case "delayed-response" =>
            val originalSender = sender()
            context.system.scheduler.scheduleOnce(200.millis.dilated) {
              originalSender ! "response"
            }
        }
      }
      
      val actor = system.actorOf(Props[TimedActor]())
      actor ! "delayed-response"
      
      // Wait with dilated timeout to account for test environment
      expectMsg(1.second.dilated, "response")
    }
  }
}

Integration with TestKit Methods

Package-level functions integrate seamlessly with TestKit methods:

class IntegrationTest extends TestKit(ActorSystem("TestSystem")) with ImplicitSender {
  "Package functions with TestKit" should {
    "combine with expectation methods" in {
      val actor = system.actorOf(Props(new Actor {
        def receive = {
          case "test" =>
            log.info("Processing test message")
            sender() ! "processed"
        }
      }))
      
      filterEvents(EventFilter.info(message = "Processing test message")) {
        actor ! "test"
        expectMsg(3.seconds.dilated, "processed")
      }
    }
    
    "work with awaitCond" in {
      @volatile var completed = false
      
      val actor = system.actorOf(Props(new Actor {
        def receive = {
          case "complete" =>
            Thread.sleep(100) // Simulate work
            completed = true
        }
      }))
      
      actor ! "complete"
      awaitCond(completed, max = 2.seconds.dilated)
      
      assert(completed)
    }
    
    "combine with within blocks" in {
      within(1.second.dilated) {
        filterEvents(EventFilter.debug(start = "Debug message")) {
          val actor = system.actorOf(Props(new Actor {
            def receive = {
              case msg => 
                log.debug(s"Debug message: $msg")
                sender() ! s"handled: $msg"
            }
          }))
          
          actor ! "test"
          expectMsg("handled: test")
        }
      }
    }
  }
}

Advanced Usage Patterns

Nested Event Filtering:

class NestedFilteringTest extends TestKit(ActorSystem("TestSystem")) {
  "Nested event filtering" should {
    "work with multiple layers" in {
      filterEvents(EventFilter.error(occurrences = 1)) {
        filterEvents(EventFilter.warning(occurrences = 2)) {
          val actor = system.actorOf(Props(new Actor {
            def receive = {
              case "process" =>
                log.warning("First warning")
                log.warning("Second warning")
                log.error("An error occurred")
            }
          }))
          
          actor ! "process"
        }
      }
    }
  }
}

Dynamic Filter Creation:

class DynamicFilterTest extends TestKit(ActorSystem("TestSystem")) {
  "Dynamic filter creation" should {
    "create filters based on test parameters" in {
      def testWithFilters(errorCount: Int, warningCount: Int): Unit = {
        val filters = Seq(
          if (errorCount > 0) Some(EventFilter.error(occurrences = errorCount)) else None,
          if (warningCount > 0) Some(EventFilter.warning(occurrences = warningCount)) else None
        ).flatten
        
        filterEvents(filters) {
          val actor = system.actorOf(Props(new Actor {
            def receive = {
              case (errors: Int, warnings: Int) =>
                (1 to errors).foreach(_ => log.error("Error"))
                (1 to warnings).foreach(_ => log.warning("Warning"))
            }
          }))
          
          actor ! (errorCount, warningCount)
        }
      }
      
      testWithFilters(2, 3)
      testWithFilters(0, 1)
      testWithFilters(1, 0)
    }
  }
}

Configuration and Environment

Package functions respect TestKit configuration:

# application.conf
akka {
  test {
    # Time dilation factor - all dilated durations multiplied by this
    timefactor = 1.0
    
    # Event filter leeway - extra time to wait for expected log events
    filter-leeway = 3s
    
    # Default timeouts
    single-expect-default = 3s
    multi-expect-default = 3s
  }
}

Accessing Configuration:

class ConfigurationAccessTest extends TestKit(ActorSystem("TestSystem")) {
  "Configuration access" should {
    "use configured time factors" in {
      val settings = TestKitExtension(system)
      val timeFactor = settings.TestTimeFactor
      
      val original = 1.second
      val dilated = original.dilated
      val expected = Duration.fromNanos((original.toNanos * timeFactor).toLong)
      
      dilated should be(expected)
    }
  }
}

Best Practices

  1. Always Use Dilated Timeouts: Use .dilated for all test timeouts to handle slow environments
  2. Specific Event Filtering: Be specific with event filters to avoid false positives
  3. Combine Functions: Mix package functions with TestKit methods for comprehensive testing
  4. Environment Awareness: Configure appropriate time factors for different test environments
// Good: Dilated timeouts for reliability
expectMsg(5.seconds.dilated, expectedMessage)
awaitCond(condition, max = 3.seconds.dilated)

// Good: Specific event filtering
filterEvents(
  EventFilter.error(source = "com.myapp.Service", occurrences = 1),
  EventFilter.warning(pattern = "deprecated.*method".r, occurrences = 1)
) {
  // test code
}

// Good: Combined with TestKit methods
within(10.seconds.dilated) {
  filterException[IllegalStateException] {
    actor ! InvalidCommand()
  }
}