Additional utility functions and classes for common testing scenarios, including socket utilities, test exceptions, and serialization helpers.
Utilities for getting free socket addresses in tests.
object SocketUtil {
// Port utilities
def temporaryLocalPort(udp: Boolean = false): Int
def temporaryLocalAddress(udp: Boolean = false): InetSocketAddress
// Server address utilities
def temporaryServerAddress(address: String = "127.0.0.1", udp: Boolean = false): InetSocketAddress
def temporaryServerAddresses(
numberOfAddresses: Int,
hostname: String = "127.0.0.1",
udp: Boolean = false
): Seq[InetSocketAddress]
// Unbound address utility
def notBoundServerAddress(address: String = "127.0.0.1", udp: Boolean = false): InetSocketAddress
}Usage Examples:
import akka.testkit.SocketUtil
class NetworkingTest extends TestKit(ActorSystem("TestSystem")) {
"SocketUtil" should {
"provide free TCP ports" in {
val port1 = SocketUtil.temporaryLocalPort()
val port2 = SocketUtil.temporaryLocalPort()
port1 should not equal port2
port1 should be > 1024
port2 should be > 1024
// Use ports for testing
val server = startTestServer(port1)
val client = connectToServer("localhost", port1)
// Test networking code
client.send("hello")
server.expectReceive("hello")
}
"provide free UDP ports" in {
val udpPort = SocketUtil.temporaryLocalPort(udp = true)
val udpAddress = SocketUtil.temporaryLocalAddress(udp = true)
udpAddress.getPort should equal(udpPort)
// Use for UDP testing
val udpSocket = new DatagramSocket(udpPort)
// Test UDP functionality
}
"provide server addresses" in {
val address = SocketUtil.temporaryServerAddress("0.0.0.0")
address.getHostString should be("0.0.0.0")
address.getPort should be > 1024
// Multiple addresses for cluster testing
val addresses = SocketUtil.temporaryServerAddresses(3, "127.0.0.1")
addresses should have size 3
addresses.map(_.getPort).distinct should have size 3
}
}
}Predefined exception for tests without stack trace.
final case class TestException(message: String) extends RuntimeException(message) with NoStackTraceUsage Examples:
import akka.testkit.TestException
class ExceptionTest extends TestKit(ActorSystem("TestSystem")) {
"TestException" should {
"be lightweight for testing" in {
val exception = TestException("Test error message")
exception.getMessage should be("Test error message")
exception.getStackTrace should be(empty) // No stack trace for performance
// Use in actor testing
EventFilter[TestException](message = "Test error message").intercept {
val actor = system.actorOf(Props(new Actor {
def receive = {
case "throw" => throw TestException("Test error message")
}
}))
actor ! "throw"
}
}
"be useful for expected failures" in {
class FailingActor extends Actor {
def receive = {
case "fail" => throw TestException("Expected failure")
case msg => sender() ! msg
}
}
val actor = system.actorOf(Props[FailingActor]())
// Test normal operation
actor ! "hello"
expectMsg("hello")
// Test expected failure
EventFilter[TestException](message = "Expected failure").intercept {
actor ! "fail"
}
}
}
}Utilities for Java serialization in tests.
// Marker trait for test messages using Java serialization
trait JavaSerializable
// Java serializer for ad-hoc test messages
class TestJavaSerializer extends Serializer {
def identifier: Int
def includeManifest: Boolean
def toBinary(o: AnyRef): Array[Byte]
def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]]): AnyRef
}Usage Examples:
import akka.testkit.JavaSerializable
// Test message with Java serialization
case class TestMessage(data: String, id: Int) extends JavaSerializable
class SerializationTest extends TestKit(ActorSystem("TestSystem")) {
"Java serialization" should {
"work with test messages" in {
val message = TestMessage("test data", 123)
val actor = system.actorOf(Props(new Actor {
def receive = {
case msg: TestMessage => sender() ! msg.copy(data = msg.data.toUpperCase)
}
}))
actor ! message
expectMsg(TestMessage("TEST DATA", 123))
}
}
}Utility methods for TestKit operations.
object TestKitUtils {
// Extract test name from call stack
def testNameFromCallStack(classToStartFrom: Class[_], fallback: String): String
}Usage Example:
import akka.testkit.TestKitUtils
class UtilsTest extends TestKit(ActorSystem("TestSystem")) {
"TestKitUtils" should {
"extract test names" in {
val testName = TestKitUtils.testNameFromCallStack(classOf[UtilsTest], "default-test")
testName should include("extract test names")
// Useful for dynamic actor naming
val actor = system.actorOf(Props[EchoActor](), s"echo-$testName")
actor.path.name should startWith("echo-")
}
}
}Helper functions for test configuration management.
// Access test configuration
def testKitSettings(implicit system: ActorSystem): TestKitSettings = TestKitExtension(system)
// Time dilation helpers (from package object)
implicit class TestDuration(duration: FiniteDuration) {
def dilated(implicit system: ActorSystem): FiniteDuration
}Usage Examples:
import akka.testkit._
import scala.concurrent.duration._
class ConfigurationTest extends TestKit(ActorSystem("TestSystem")) {
"Configuration utilities" should {
"provide test settings" in {
val settings = testKitSettings
settings.TestTimeFactor should be >= 1.0
settings.SingleExpectDefaultTimeout should be > Duration.Zero
settings.DefaultTimeout.duration should be > Duration.Zero
}
"dilate time for tests" in {
val originalDuration = 1.second
val dilated = originalDuration.dilated
// Dilated duration accounts for slow test environments
dilated should be >= originalDuration
// Use dilated timeouts in tests
within(5.seconds.dilated) {
// Test code that might run slowly in CI environments
Thread.sleep(100)
expectNoMessage(100.millis.dilated)
}
}
}
}You can create custom utilities for your specific testing needs:
// Custom test utilities object
object MyTestUtils {
// Helper for creating actors with test-specific configuration
def testActorOf[T <: Actor](props: Props, name: String)(implicit system: ActorSystem): ActorRef = {
system.actorOf(props.withDispatcher("akka.test.calling-thread-dispatcher"), name)
}
// Helper for temporary files in tests
def withTempFile[T](content: String)(test: File => T): T = {
val file = File.createTempFile("test", ".tmp")
try {
Files.write(file.toPath, content.getBytes)
test(file)
} finally {
file.delete()
}
}
// Helper for timing operations
def timed[T](operation: => T): (T, FiniteDuration) = {
val start = System.nanoTime()
val result = operation
val duration = (System.nanoTime() - start).nanos
(result, duration)
}
}
// Usage in tests
class CustomUtilsTest extends TestKit(ActorSystem("TestSystem")) {
import MyTestUtils._
"Custom utilities" should {
"help with actor creation" in {
val actor = testActorOf(Props[EchoActor](), "test-echo")
actor ! "hello"
expectMsg("hello")
}
"provide temp file support" in {
withTempFile("test content") { file =>
file.exists() should be(true)
Source.fromFile(file).mkString should be("test content")
}
}
"measure timing" in {
val (result, duration) = timed {
Thread.sleep(100)
"completed"
}
result should be("completed")
duration should be >= 100.millis
}
}
}// Good: Use utilities for reliable testing
val port = SocketUtil.temporaryLocalPort()
val server = startServer(port)
try {
// Test code
} finally {
server.stop()
}
// Good: Use time dilation for robustness
within(5.seconds.dilated) {
heavyOperation()
expectMsg("completed")
}
// Good: Use TestException for expected failures
EventFilter[TestException](message = "Expected test failure").intercept {
actor ! TriggerFailure()
}