or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assertions.mdindex.mdmocking.mdproperty-testing.mdtest-aspects.mdtest-environment.mdtest-execution.mdtest-specifications.md
tile.json

test-execution.mddocs/

Test Execution

Test runners and executors for running test suites with configurable behavior and reporting.

Capabilities

TestRunner

Main component for executing test specifications.

/**
 * Test runner that executes test specifications
 * @tparam R - Environment requirements
 * @tparam E - Error type
 */
case class TestRunner[+R, -E](executor: TestExecutor[R, E]) {
  /** Executes a test specification */
  def run[R1 <: R, E1 >: E](spec: ZSpec[R1, E1]): URIO[R1, ExecutedSpec[E1]]
  
  /** Creates new runner with different executor */  
  def withExecutor[R1, E1](executor: TestExecutor[R1, E1]): TestRunner[R1, E1]
}

object TestRunner {
  /** Default test runner with standard test environment */
  def default[R](testEnvironment: ZLayer[Any, Nothing, R]): TestRunner[R, Any]
}

TestExecutor

Core execution engine for running tests with reporting.

/**
 * Executes test specifications with configurable behavior
 * @tparam R - Environment requirements
 * @tparam E - Error type
 */
abstract class TestExecutor[-R, -E] {
  /** Executes specification with reporter */
  def run[R1 <: R, E1 >: E](
    spec: ZSpec[R1, E1], 
    reporter: TestReporter[E1]
  ): URIO[R1, ExecutedSpec[E1]]
  
  /** Executes specification with environment layer */
  def runSpec[R1 <: R, E1 >: E](
    spec: ZSpec[R1, E1],
    environmentLayer: ZLayer[Any, Nothing, R1]
  ): UIO[ExecutedSpec[E1]]
}

object TestExecutor {
  /** Default executor with standard configuration */
  def default[R](testEnvironment: ZLayer[Any, Nothing, R]): TestExecutor[R, Any]
}

Runnable Spec Base Classes

Abstract base classes for creating executable test specifications.

/**
 * Base class for runnable test specifications
 * @tparam R - Environment requirements  
 * @tparam E - Error type
 */
abstract class RunnableSpec[-R, +E] {
  /** The test specification to run */
  def spec: ZSpec[R, E]
  
  /** Test runner configuration */  
  def runner: TestRunner[R, E]
  
  /** Test aspects to apply to all tests */
  def aspects: List[TestAspect[Nothing, R, Nothing, Any]]
  
  /** Platform-specific configuration */
  def platform: TestPlatform
  
  /** Executes the specification */
  def runSpec(spec: ZSpec[R, E]): URIO[R, ExecutedSpec[E]]
}

/**
 * Default runnable spec with test environment
 */
abstract class DefaultRunnableSpec extends RunnableSpec[TestEnvironment, Any] {
  /** Test specification with standard test environment */
  def spec: ZSpec[TestEnvironment, Any]
  
  /** Default aspects including timeout warning */
  override def aspects: List[TestAspect[Nothing, TestEnvironment, Nothing, Any]] =
    List(TestAspect.timeoutWarning(60.seconds))
    
  /** Default test runner */
  override def runner: TestRunner[TestEnvironment, Any] = defaultTestRunner
}

/**
 * Mutable runnable spec for dynamic test creation
 */
abstract class MutableRunnableSpec extends RunnableSpec[TestEnvironment, Any] {
  /** Mutable test specification */
  def spec: ZSpec[TestEnvironment, Any]
}

ExecutedSpec

Result of test execution with hierarchical structure.

/**
 * Result of executing a test specification
 * @tparam E - Error type
 */
case class ExecutedSpec[+E](
  caseValue: SpecCase[Any, TestFailure[E], TestSuccess, ExecutedSpec[E]]
) {
  /** Folds over the execution tree */
  def fold[Z](f: ExecutedSpec[E] => Z): Z
  
  /** Checks if any spec matches predicate */
  def exists(f: ExecutedSpec[E] => Boolean): Boolean
  
  /** Gets all failed specs */
  def failures: List[ExecutedSpec[E]]
  
  /** Gets all successful specs */  
  def successes: List[ExecutedSpec[E]]
  
  /** Gets all ignored specs */
  def ignored: List[ExecutedSpec[E]]
  
  /** Counts total number of tests */
  def size: Int
  
  /** Transforms the execution result */
  def transform[E1](f: SpecCase[Any, TestFailure[E], TestSuccess, ExecutedSpec[E]] => 
                      SpecCase[Any, TestFailure[E1], TestSuccess, ExecutedSpec[E1]]): ExecutedSpec[E1]
}

Test Reporting

System for reporting test results with customizable output.

/**
 * Reporter for test execution results
 * @tparam E - Error type
 */
type TestReporter[-E] = (Duration, ExecutedSpec[E]) => URIO[TestLogger, Unit]

object TestReporter {
  /** Silent reporter that produces no output */
  val silent: TestReporter[Any]
}

/**
 * Default test reporter with console output
 */
object DefaultTestReporter {
  /** Standard console reporter */
  def apply[E](duration: Duration, executedSpec: ExecutedSpec[E]): URIO[TestLogger, Unit]
}

Summary and Results

Summary information about test execution.

/**
 * Summary of test execution results
 */
case class Summary(
  success: Int,      // Number of successful tests
  fail: Int,         // Number of failed tests  
  ignore: Int,       // Number of ignored tests
  summary: String    // Textual summary
) {
  /** Total number of tests */
  def total: Int = success + fail + ignore
  
  /** Whether all tests passed */
  def isSuccess: Boolean = fail == 0
}

object Summary {
  /** Creates summary from executed spec */
  def fromExecutedSpec[E](executedSpec: ExecutedSpec[E]): Summary
}

/**
 * Builder for constructing test summaries
 */
class SummaryBuilder {
  /** Adds successful test */
  def addSuccess(): SummaryBuilder
  
  /** Adds failed test */
  def addFailure(): SummaryBuilder
  
  /** Adds ignored test */
  def addIgnored(): SummaryBuilder
  
  /** Builds final summary */
  def build(): Summary
}

Platform Configuration

Platform-specific test execution configuration.

/**
 * Platform-specific test configuration
 */
trait TestPlatform {
  /** Whether platform is JVM */
  def isJVM: Boolean
  
  /** Whether platform is JavaScript */  
  def isJS: Boolean
  
  /** Whether platform is Native */
  def isNative: Boolean
  
  /** Platform-specific test execution timeout */
  def timeout: Duration
}

object TestPlatform {
  /** Default platform configuration */
  val default: TestPlatform
  
  /** Check if running on JVM */
  def isJVM: Boolean
  
  /** Check if running on JavaScript */
  def isJS: Boolean
  
  /** Check if running on Native */
  def isNative: Boolean
}

Usage Examples

Basic Runnable Spec

import zio.test._
import zio.test.environment.TestEnvironment

object BasicSpec extends DefaultRunnableSpec {
  def spec = suite("Basic Tests")(
    test("simple test") {
      assert(2 + 2)(equalTo(4))
    },
    
    testM("effectful test") {
      assertM(ZIO.succeed(42))(equalTo(42))
    }
  )
}

Custom Runner Configuration

import zio.test._
import zio.duration._

object CustomRunnerSpec extends RunnableSpec[TestEnvironment, Any] {
  def spec = suite("Custom Runner Tests")(
    // test definitions...
  )
  
  override def aspects = List(
    TestAspect.timeout(30.seconds),
    TestAspect.parallel,
    TestAspect.retryN(3)
  )
  
  override def runner = TestRunner(
    TestExecutor.default(testEnvironment)
  )
}

Mutable Spec for Dynamic Tests

object DynamicSpec extends MutableRunnableSpec {
  def spec = {
    val dynamicTests = (1 to 5).map { i =>
      test(s"dynamic test $i") {
        assert(i * 2)(isGreaterThan(i))
      }
    }
    
    suite("Dynamic Tests")(dynamicTests: _*)
  }
}

Custom Test Reporter

import zio.test._

val customReporter: TestReporter[Any] = (duration, executedSpec) => {
  for {
    summary <- ZIO.succeed(Summary.fromExecutedSpec(executedSpec))
    _       <- TestLogger.logLine(s"Tests completed in ${duration.toMillis}ms")
    _       <- TestLogger.logLine(s"Passed: ${summary.success}")
    _       <- TestLogger.logLine(s"Failed: ${summary.fail}")
    _       <- TestLogger.logLine(s"Ignored: ${summary.ignore}")
  } yield ()
}

object CustomReporterSpec extends DefaultRunnableSpec {
  def spec = suite("Custom Reporter Tests")(
    // test definitions...
  )
  
  override def runner = TestRunner(
    TestExecutor.default(testEnvironment)
  ).withReporter(customReporter)
}

Platform-Specific Tests

object PlatformSpec extends DefaultRunnableSpec {
  def spec = suite("Platform Tests")(
    test("JVM only test") {
      assertTrue(TestPlatform.isJVM)
    } @@ TestAspect.jvmOnly,
    
    test("JS only test") {
      assertTrue(TestPlatform.isJS)  
    } @@ TestAspect.jsOnly,
    
    suite("Cross-platform tests")(
      test("works everywhere") {
        assert(2 + 2)(equalTo(4))
      }
    )
  )
}

Running Tests Programmatically

import zio.test._

val mySpec = suite("Programmatic Tests")(
  test("test 1")(assert(true)(isTrue)),
  test("test 2")(assert(false)(isFalse))
)

val program = for {
  runner <- ZIO.succeed(defaultTestRunner)
  result <- runner.run(mySpec)
  summary = Summary.fromExecutedSpec(result)
  _      <- console.putStrLn(s"Tests: ${summary.total}, Passed: ${summary.success}")
} yield summary

// Run with test environment
program.provideLayer(testEnvironment ++ Console.live)

Test Execution with Timeout

object TimeoutSpec extends DefaultRunnableSpec {
  def spec = suite("Timeout Tests")(
    testM("fast test") {
      assertM(ZIO.succeed(42))(equalTo(42))
    },
    
    testM("slow test") {
      for {
        _ <- ZIO.sleep(10.seconds)
        result <- ZIO.succeed(42)
      } yield assert(result)(equalTo(42))
    } @@ TestAspect.timeout(5.seconds)
  )
  
  override def aspects = List(
    TestAspect.timeoutWarning(2.seconds)
  )
}