or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

async.mdfixtures.mdindex.mdmatchers.mdproperty.mdscalactic.mdtest-styles.md
tile.json

fixtures.mddocs/

Fixtures and Lifecycle

ScalaTest provides comprehensive support for test fixtures and suite lifecycle management, enabling setup and teardown operations, resource management, and data sharing between tests.

Capabilities

Before and After Each Test

Execute setup and cleanup code before and after each individual test.

trait BeforeAndAfterEach extends SuiteMixin {
  
  /**
   * Execute before each test method
   */
  def beforeEach(): Unit = ()
  
  /**
   * Execute after each test method
   */
  def afterEach(): Unit = ()
  
  /**
   * Override to customize the execution around each test
   */
  abstract override def withFixture(test: NoArgTest): Outcome = {
    beforeEach()
    try super.withFixture(test)
    finally afterEach()
  }
}

Usage Examples:

import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.BeforeAndAfterEach

class DatabaseSuite extends AnyFunSuite with BeforeAndAfterEach {
  var database: Database = _
  
  override def beforeEach(): Unit = {
    database = new Database()
    database.connect()
    database.createTables()
  }
  
  override def afterEach(): Unit = {
    database.dropTables()
    database.disconnect()
    database = null
  }
  
  test("should insert user") {
    val user = User("John", "john@example.com")
    database.insert(user)
    database.count("users") should equal (1)
  }
  
  test("should delete user") {
    val user = User("Jane", "jane@example.com")  
    database.insert(user)
    database.delete(user.id)
    database.count("users") should equal (0)
  }
}

Before and After All Tests

Execute setup and cleanup code once before and after all tests in the suite.

trait BeforeAndAfterAll extends SuiteMixin {
  
  /**
   * Execute once before all tests in the suite
   */
  def beforeAll(): Unit = ()
  
  /**
   * Execute once after all tests in the suite
   */
  def afterAll(): Unit = ()
  
  /**
   * Override to customize suite-wide setup/teardown
   */
  abstract override def run(testName: Option[String], args: Args): Status = {
    if (!args.runTestInNewInstance) beforeAll()
    try {
      val status = super.run(testName, args)
      status.waitUntilCompleted()
      status
    } finally {
      if (!args.runTestInNewInstance) afterAll()  
    }
  }
}

Usage Examples:

import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.BeforeAndAfterAll

class ServerSuite extends AnyFunSuite with BeforeAndAfterAll {
  var server: TestServer = _
  
  override def beforeAll(): Unit = {
    server = new TestServer(port = 8080)
    server.start()
    server.waitUntilReady()
  }
  
  override def afterAll(): Unit = {
    server.stop()
    server.waitUntilStopped()
  }
  
  test("server should respond to health check") {
    val response = httpGet(s"http://localhost:8080/health")
    response.status should equal (200)
    response.body should include ("OK")
  }
  
  test("server should handle API requests") {
    val response = httpPost(s"http://localhost:8080/api/users", """{"name": "test"}""")
    response.status should equal (201)
  }
}

Fixture Test Suites

Share fixtures between tests using parameterized test functions.

trait FixtureTestSuite extends TestSuite {
  
  /**
   * The type of fixture object passed to tests
   */
  type FixtureParam
  
  /**
   * Create and cleanup fixture for each test
   */
  def withFixture(test: OneArgTest): Outcome
  
  /**
   * Run a single test with fixture
   */
  def runTest(testName: String, args: Args): Status = {
    val oneArgTest = new OneArgTest {
      val name = testName
      def apply(fixture: FixtureParam): Outcome = {
        // Test implementation provided by concrete suite
      }
    }
    withFixture(oneArgTest).toStatus
  }
}

/**
 * Fixture-enabled FunSuite
 */
abstract class FixtureAnyFunSuite extends FixtureTestSuite with TestRegistration {
  
  /**
   * Register a test that requires a fixture
   */
  protected def test(testName: String)(testFun: FixtureParam => Any): Unit
}

Usage Examples:

import org.scalatest.fixture.AnyFunSuite
import java.io.{File, FileWriter}

class FileFixtureSuite extends AnyFunSuite {
  
  // Fixture is a temporary file
  type FixtureParam = File
  
  def withFixture(test: OneArgTest): Outcome = {
    val tempFile = File.createTempFile("test", ".txt")
    
    try {
      // Setup: write initial content
      val writer = new FileWriter(tempFile)
      writer.write("initial content")
      writer.close()
      
      // Run test with fixture
      test(tempFile)
    } finally {
      // Cleanup: delete temp file
      tempFile.delete()
    }
  }
  
  test("should read file content") { file =>
    val content = scala.io.Source.fromFile(file).mkString
    content should include ("initial")
  }
  
  test("should append to file") { file =>
    val writer = new FileWriter(file, true)  // append mode
    writer.write("\nappended content")
    writer.close()
    
    val content = scala.io.Source.fromFile(file).mkString
    content should include ("appended")
  }
}

Fixture Context

Lightweight fixture sharing using traits.

trait FixtureContext {
  // Define fixture data and helper methods as trait members
}

Usage Examples:

import org.scalatest.funsuite.AnyFunSuite

class ContextFixtureSuite extends AnyFunSuite {
  
  trait DatabaseContext {
    val database = new InMemoryDatabase()
    database.createSchema()
    
    val testUser = User("testuser", "test@example.com")
    database.insert(testUser)
    
    def findUser(name: String): Option[User] = database.findByName(name)
  }
  
  trait ApiContext {
    val baseUrl = "http://test.example.com"
    val apiKey = "test-api-key"
    
    def makeRequest(endpoint: String): HttpResponse = {
      // Mock HTTP request implementation
      HttpResponse(200, s"Response from $endpoint")
    }
  }
  
  test("should find existing user") {
    new DatabaseContext {
      findUser("testuser") should be (defined)
      findUser("nonexistent") should be (empty)
    }
  }
  
  test("should handle API requests") {
    new ApiContext {
      val response = makeRequest("/users")
      response.status should equal (200)
      response.body should include ("Response from /users")
    }
  }
  
  test("should combine contexts") {
    new DatabaseContext with ApiContext {
      // Use both database and API fixtures
      val user = findUser("testuser").get
      val response = makeRequest(s"/users/${user.id}")
      response.status should equal (200)
    }
  }
}

Test Data and Config Map

Access configuration and test data through the Args parameter.

trait BeforeAndAfterEachTestData extends SuiteMixin {
  
  /**
   * Execute before each test with access to test data
   */
  def beforeEach(testData: TestData): Unit = ()
  
  /**
   * Execute after each test with access to test data
   */
  def afterEach(testData: TestData): Unit = ()
  
  abstract override def withFixture(test: NoArgTest): Outcome = {
    beforeEach(test)
    try super.withFixture(test)
    finally afterEach(test)
  }
}

trait BeforeAndAfterAllConfigMap extends SuiteMixin {
  
  /**
   * Execute before all tests with access to config map
   */
  def beforeAll(configMap: ConfigMap): Unit = ()
  
  /**
   * Execute after all tests with access to config map
   */
  def afterAll(configMap: ConfigMap): Unit = ()
}

/**
 * Test metadata and configuration
 */
trait TestData {
  val name: String
  val scopes: Vector[String]
  val text: String
  val tags: Set[String]
  val pos: Option[source.Position]
}

/**
 * Configuration map for test execution
 */
class ConfigMap(map: Map[String, Any]) {
  def apply(key: String): Any = map(key)
  def get(key: String): Option[Any] = map.get(key)
  def contains(key: String): Boolean = map.contains(key)
  def ++(other: ConfigMap): ConfigMap = new ConfigMap(map ++ other.map)
}

Usage Examples:

import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.{BeforeAndAfterEachTestData, BeforeAndAfterAllConfigMap}

class ConfigurableSuite extends AnyFunSuite 
  with BeforeAndAfterEachTestData 
  with BeforeAndAfterAllConfigMap {
  
  var globalConfig: String = _
  
  override def beforeAll(configMap: ConfigMap): Unit = {
    globalConfig = configMap.getOrElse("environment", "test").toString
    println(s"Running tests in $globalConfig environment")
  }
  
  override def beforeEach(testData: TestData): Unit = {
    println(s"Starting test: ${testData.name}")
    if (testData.tags.contains("slow")) {
      println("This is a slow test, please be patient...")
    }
  }
  
  override def afterEach(testData: TestData): Unit = {
    println(s"Completed test: ${testData.name}")
  }
  
  test("should use global config") {
    globalConfig should not be empty
    // Test implementation using globalConfig
  }
  
  test("slow database operation", SlowTest) {
    // This test will get special logging due to the SlowTest tag
    Thread.sleep(100)  // Simulate slow operation
    succeed
  }
}

// Custom tag for marking slow tests
object SlowTest extends Tag("org.example.SlowTest")

Types

/**
 * Test function that receives no fixture parameter
 */
trait NoArgTest extends (() => Outcome) with TestData {
  def apply(): Outcome
  val name: String
  val scopes: Vector[String]  
  val text: String
  val tags: Set[String]
}

/**
 * Test function that receives one fixture parameter
 */
trait OneArgTest extends (FixtureParam => Outcome) with TestData {
  def apply(fixture: FixtureParam): Outcome
  val name: String
  val scopes: Vector[String]
  val text: String  
  val tags: Set[String]
}

/**
 * Mixin trait for suites that can be mixed into other suites
 */
trait SuiteMixin { this: Suite =>
  // Mixed into Suite to provide additional functionality
}

/**
 * Test outcome representing the result of running a test
 */
sealed abstract class Outcome extends Product with Serializable {
  def isSucceeded: Boolean
  def isFailed: Boolean
  def isCanceled: Boolean
  def isPending: Boolean
  def toStatus: Status
}

case object Succeeded extends Outcome
final case class Failed(exception: Throwable) extends Outcome  
final case class Canceled(exception: Throwable) extends Outcome
case object Pending extends Outcome