Compile-time smart assertions with automatic expression analysis and enhanced error reporting. Smart assertions provide more intuitive test writing with better error messages by analyzing expressions at compile time.
The primary smart assertion function that analyzes boolean expressions at compile time.
/**
* Smart assertion with compile-time expression analysis
* @param assertion - Boolean expression to test (analyzed at compile time)
* @return TestResult with detailed error reporting
*/
inline def assertTrue(inline assertion: Boolean)(implicit
trace: Trace,
sourceLocation: SourceLocation
): TestResultUsage Examples:
import zio.test._
test("smart assertions") {
val user = User("Alice", 25, active = true)
val numbers = List(1, 2, 3, 4, 5)
// Simple comparisons with enhanced error messages
assertTrue(user.age >= 18)
assertTrue(user.name.nonEmpty)
assertTrue(user.active)
// Complex expressions are analyzed
assertTrue(numbers.size == 5 && numbers.contains(3))
assertTrue(user.age > 20 && user.name.startsWith("A"))
// Method calls are captured
assertTrue(numbers.head == 1)
assertTrue(numbers.last == 5)
assertTrue(user.name.length > 2)
}Smart assertion that applies a traditional assertion to a value with compile-time expression capture.
/**
* Smart assertion for single values with traditional assertions
* @param value - Value to test (expression captured at compile time)
* @param assertion - Assertion to apply to the value
* @return TestResult with enhanced error reporting
*/
inline def assert[A](inline value: A)(inline assertion: Assertion[A])(implicit
trace: Trace,
sourceLocation: SourceLocation
): TestResultUsage Examples:
import zio.test._
import zio.test.Assertion._
test("smart assert with explicit assertions") {
val numbers = List(1, 2, 3, 4, 5)
val name = "Alice"
val age = 25
// Explicit assertions with smart error reporting
assert(numbers)(hasSize(equalTo(5)))
assert(numbers)(contains(3))
assert(numbers)(forall(isGreaterThan(0)))
assert(name)(hasLength(equalTo(5)))
assert(name)(startsWith("A"))
assert(age)(isGreaterThanEqualTo(18))
assert(age)(isLessThan(100))
}Smart assertion for effectful computations with compile-time expression analysis.
/**
* Smart assertion for effectful values
* @param effect - ZIO effect producing value to test
* @param assertion - Assertion to apply to the effect result
* @return ZIO effect producing TestResult
*/
inline def assertZIO[R, E, A](effect: ZIO[R, E, A])(inline assertion: Assertion[A])(implicit
trace: Trace,
sourceLocation: SourceLocation
): ZIO[R, E, TestResult]Usage Examples:
import zio.test._
import zio.test.Assertion._
test("smart assertZIO") {
// Testing effectful computations
assertZIO(ZIO.succeed(42))(equalTo(42)) *>
assertZIO(ZIO.succeed("hello"))(hasLength(equalTo(5))) *>
assertZIO(ZIO.succeed(List(1, 2, 3)))(hasSize(equalTo(3)))
}
test("assertZIO with service calls") {
for {
// Testing service method results
result1 <- assertZIO(userService.findById(1))(isSome(anything))
result2 <- assertZIO(userService.count())(isGreaterThan(0))
result3 <- assertZIO(mathService.add(2, 3))(equalTo(5))
} yield result1 && result2 && result3
}Compile-time type checking for ensuring code compiles or fails to compile as expected.
/**
* Compile-time type checking (Scala 3)
* @param code - Code to check for compilation
* @return UIO[Either[String, Unit]] - Left with error messages if compilation fails, Right with Unit if successful
*/
inline def typeCheck(inline code: String): UIO[Either[String, Unit]]
/**
* Compile-time type checking (Scala 2)
* @param code - Code to check for compilation
* @return UIO[Either[String, Unit]] - Left with error messages if compilation fails, Right with Unit if successful
*/
def typeCheck(code: String): UIO[Either[String, Unit]]Usage Examples:
import zio.test._
test("type checking") {
for {
// Ensure valid code compiles
validResult <- typeCheck("val x: Int = 42")
_ <- assertTrue(validResult.isRight)
// Ensure invalid code fails to compile
invalidResult <- typeCheck("val x: String = 42")
_ <- assertTrue(invalidResult.isLeft)
// Test generic type inference
genericResult <- typeCheck("List(1, 2, 3).map(_ * 2)")
_ <- assertTrue(genericResult.isRight)
} yield ()
}Extension methods for navigating complex data structures in smart assertions.
/**
* Extension method for navigating data in smart assertions
*/
implicit final class SmartAssertionOps[A](private val self: A) extends AnyVal {
/**
* Transform the value using the given TestLens for navigation
* @param f - Function to transform TestLens
* @return Transformed value for assertion
*/
def is[B](f: TestLens[A] => TestLens[B]): B
}
/**
* TestLens for navigating Option values
*/
implicit final class TestLensOptionOps[A](private val self: TestLens[Option[A]]) extends AnyVal {
/** Transform Option to its Some value, fails if None */
def some: TestLens[A]
}
/**
* TestLens for navigating Either values
*/
implicit final class TestLensEitherOps[E, A](private val self: TestLens[Either[E, A]]) extends AnyVal {
/** Transform Either to its Left value, fails if Right */
def left: TestLens[E]
/** Transform Either to its Right value, fails if Left */
def right: TestLens[A]
}
/**
* TestLens for navigating Try values
*/
implicit final class TestLensTryOps[A](private val self: TestLens[scala.util.Try[A]]) extends AnyVal {
/** Transform Try to its Success value, fails if Failure */
def success: TestLens[A]
/** Transform Try to Throwable if Failure, fails if Success */
def failure: TestLens[Throwable]
}
/**
* TestLens for navigating Exit values
*/
implicit final class TestLensExitOps[E, A](private val self: TestLens[Exit[E, A]]) extends AnyVal {
/** Transform Exit to Throwable if die, fails otherwise */
def die: TestLens[Throwable]
/** Transform Exit to failure type if fail, fails otherwise */
def failure: TestLens[E]
/** Transform Exit to success type if succeed, fails otherwise */
def success: TestLens[A]
/** Transform Exit to its underlying Cause if it has one */
def cause: TestLens[Cause[E]]
/** Transform Exit to boolean representing if interrupted */
def interrupted: TestLens[Boolean]
}Usage Examples:
import zio.test._
test("smart assertion navigation") {
val maybeUser: Option[User] = Some(User("Alice", 25))
val result: Either[String, Int] = Right(42)
val computation: Try[String] = Success("hello")
// Navigate Option values
assertTrue(maybeUser.is(_.some.name) == "Alice")
assertTrue(maybeUser.is(_.some.age) > 18)
// Navigate Either values
assertTrue(result.is(_.right) == 42)
assertTrue(result.is(_.right) > 40)
// Navigate Try values
assertTrue(computation.is(_.success.length) == 5)
assertTrue(computation.is(_.success.startsWith("h")))
}
test("exit navigation") {
val success: Exit[String, Int] = Exit.succeed(42)
val failure: Exit[String, Int] = Exit.fail("error")
assertTrue(success.is(_.success) == 42)
assertTrue(failure.is(_.failure) == "error")
}Interface for creating custom smart assertion transformations.
/**
* Interface for custom assertion transformations
*/
trait CustomAssertion[A, B] {
def apply(value: A): Either[String, B]
}
/**
* TestLens extension for custom assertions
*/
implicit final class TestLensAnyOps[A](private val self: TestLens[A]) extends AnyVal {
/** Transform value with custom assertion */
def custom[B](customAssertion: CustomAssertion[A, B]): TestLens[B]
/** Transform value to subtype if possible */
def subtype[Subtype <: A]: TestLens[Subtype]
/** Always returns true if preceding transformations succeeded */
def anything: TestLens[Boolean]
}Usage Examples:
import zio.test._
// Custom assertion for email validation
val emailAssertion = new CustomAssertion[String, String] {
def apply(value: String): Either[String, String] =
if (value.contains("@")) Right(value)
else Left("Not a valid email")
}
test("custom assertions") {
val email = "user@example.com"
val invalidEmail = "notanemail"
assertTrue(email.is(_.custom(emailAssertion).nonEmpty))
// invalidEmail.is(_.custom(emailAssertion)) would fail the test
// Subtype checking
val shapes: List[Shape] = List(Circle(5), Rectangle(4, 6))
assertTrue(shapes.head.is(_.subtype[Circle].radius) == 5)
}