ZIO Test is a featherweight testing library for effectful programs
npx @tessl/cli install tessl/maven-dev-zio--zio-test_2-13@1.0.0ZIO Test is a featherweight testing library for effectful programs. It treats every test specification as an immutable value, enabling powerful composition and transformation capabilities. The library provides tight integration with ZIO effects, allowing developers to test asynchronous and complex computational workflows naturally without the need for complex async value wrapping or multiple unsafeRun calls.
libraryDependencies += "dev.zio" %% "zio-test" % "1.0.18"import zio.test._
import zio.test.Assertion._
import zio.test.environment._For specific functionality:
// Core testing DSL
import zio.test.{test, testM, suite, assert, assertM, assertTrue}
// Assertions
import zio.test.Assertion.{equalTo, isTrue, contains, hasSize}
// Property testing
import zio.test.{Gen, check, checkAll}
// Test environment
import zio.test.environment.{TestEnvironment, TestClock, TestConsole, TestRandom}
// Runnable specs
import zio.test.DefaultRunnableSpecimport zio.test._
import zio.test.Assertion._
import zio.test.environment._
object MyTestSpec extends DefaultRunnableSpec {
def spec = suite("My Test Suite")(
test("basic assertion") {
assert(2 + 2)(equalTo(4))
},
testM("effectful test") {
for {
result <- ZIO.succeed(42)
} yield assert(result)(equalTo(42))
},
test("boolean assertion") {
assertTrue(2 + 2 == 4, "hello".length == 5)
}
)
}ZIO Test is built around several key components:
Spec and ZSpec types that compose into test suites and individual testsGen generators with automatic shrinking for property-based testingCore DSL for defining test suites and individual tests with full ZIO integration and composition support.
def test(label: String)(assertion: => TestResult)(implicit loc: SourceLocation): ZSpec[Any, Nothing]
def testM[R, E](label: String)(assertion: => ZIO[R, E, TestResult])(implicit loc: SourceLocation): ZSpec[R, E]
def suite[R, E, T](label: String)(specs: Spec[R, E, T]*): Spec[R, E, T]
def suiteM[R, E, T](label: String)(specs: ZIO[R, E, Iterable[Spec[R, E, T]]]): Spec[R, E, T]Rich assertion system with over 50 built-in assertions, logical combinators, and detailed failure reporting.
def assert[A](value: => A)(assertion: Assertion[A]): TestResult
def assertM[R, E, A](effect: ZIO[R, E, A])(assertion: AssertionM[A]): ZIO[R, E, TestResult]
def assertTrue(exprs: Boolean*): TestResult
trait Assertion[A] {
def run(actual: A): AssertResult
def &&(that: Assertion[A]): Assertion[A]
def ||(that: Assertion[A]): Assertion[A]
def unary_!: Assertion[A]
}Comprehensive property-based testing with generators, automatic shrinking, and configurable test execution.
def check[R <: TestConfig, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult]
def checkM[R <: TestConfig, R1 <: R, E, A](rv: Gen[R, A])(test: A => ZIO[R1, E, TestResult]): ZIO[R1, E, TestResult]
def checkAll[R <: TestConfig, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult]
abstract class Gen[-R, +A] {
def map[B](f: A => B): Gen[R, B]
def flatMap[R1 <: R, B](f: A => Gen[R1, B]): Gen[R1, B]
def filter(f: A => Boolean): Gen[R, A]
}Controllable test services that replace ZIO's standard environment for deterministic testing.
object TestClock {
def adjust(duration: Duration): URIO[TestClock, Unit]
def setDateTime(dateTime: OffsetDateTime): URIO[TestClock, Unit]
def setTime(duration: Duration): URIO[TestClock, Unit]
def sleeps: URIO[TestClock, List[Duration]]
}
object TestConsole {
def feedLines(lines: String*): URIO[TestConsole, Unit]
def output: URIO[TestConsole, Vector[String]]
def clearOutput: URIO[TestConsole, Unit]
def clearInput: URIO[TestConsole, Unit]
}
object TestRandom {
def feedInts(ints: Int*): URIO[TestRandom, Unit]
def feedDoubles(doubles: Double*): URIO[TestRandom, Unit]
def setSeed(seed: Long): URIO[TestRandom, Unit]
}
object TestSystem {
def putProperty(name: String, value: String): URIO[TestSystem, Unit]
def putEnv(name: String, value: String): URIO[TestSystem, Unit]
def clearProperty(prop: String): URIO[TestSystem, Unit]
def clearEnv(variable: String): URIO[TestSystem, Unit]
}Complete mocking system for creating controlled test doubles with expectation-based verification.
abstract class Mock[-M <: Has[_]] {
def expects[I, A](capability: Capability[M, I, A]): Expectation[I]
}
sealed trait Expectation[-I] {
def and[I1 <: I](that: Expectation[I1]): Expectation[I1]
def repeats(range: Range): Expectation[I]
def returns[A](value: A): Expectation[I]
def returnsM[R, E, A](effect: ZIO[R, E, A]): Expectation[I]
}Test runners and executors for running test suites with configurable behavior and reporting.
case class TestRunner[+R, -E](executor: TestExecutor[R, E]) {
def run[R1 <: R, E1 >: E](spec: ZSpec[R1, E1]): URIO[R1, ExecutedSpec[E1]]
}
abstract class DefaultRunnableSpec extends RunnableSpec[TestEnvironment, Any] {
def spec: ZSpec[TestEnvironment, Any]
}Composable test transformations for modifying test behavior including retry, timeout, and parallel execution.
abstract class TestAspect[+LowerR, -UpperR, +LowerE, -UpperE] {
def apply[R, E](spec: ZSpec[R, E]): ZSpec[R, E]
}
// Control aspects
val ignore: TestAspectPoly
val flaky: TestAspectPoly
val parallel: TestAspectPoly
def timeout(duration: Duration): TestAspectAtLeastR[Live]
def eventually: TestAspectAtLeastR[Live with Annotations]
// Configuration aspects
def samples(n: Int): TestAspectAtLeastR[TestConfig]
def shrinks(n: Int): TestAspectAtLeastR[TestConfig]
def repeats(n: Int): TestAspectAtLeastR[TestConfig]
def retries(n: Int): TestAspectAtLeastR[TestConfig]
// Lifecycle aspects
def before[R](effect: ZIO[R, Nothing, Any]): TestAspectAtLeastR[R]
def after[R](effect: ZIO[R, Nothing, Any]): TestAspectAtLeastR[R]
def around[R, A](before: ZIO[R, Nothing, A])(after: A => ZIO[R, Nothing, Any]): TestAspectAtLeastR[R]type ZTest[-R, +E] = ZIO[R, TestFailure[E], TestSuccess]
type ZSpec[-R, +E] = Spec[R, TestFailure[E], TestSuccess]
type TestResult = BoolAlgebra[AssertionResult]
type ZTestEnv = TestClock with TestConsole with TestRandom with TestSystem
type TestEnvironment = ZTestEnv with Live
type Annotated[+A] = (A, TestAnnotationMap)
// Core service types
type TestConfig = Has[TestConfig.Service]
type Annotations = Has[Annotations.Service]
type Sized = Has[Sized.Service]
type TestLogger = Has[TestLogger.Service]
// Aspect type aliases
type TestAspectPoly = TestAspect[Nothing, Any, Nothing, Any]
type TestAspectAtLeastR[R] = TestAspect[Nothing, R, Nothing, Any]
case class Spec[-R, +E, +T](caseValue: SpecCase[R, E, T, Spec[R, E, T]])
sealed trait TestSuccess
object TestSuccess {
case class Succeeded(result: BoolAlgebra[Unit]) extends TestSuccess
case object Ignored extends TestSuccess
}
sealed trait TestFailure[+E]
object TestFailure {
case class Assertion(result: BoolAlgebra[AssertionResult]) extends TestFailure[Nothing]
case class Runtime[+E](cause: Cause[E]) extends TestFailure[E]
}