ScalaTest's assertion framework provides the foundation for all test verification with enhanced error reporting, stack trace management, and integration with the matcher system. The framework distinguishes between assertions (which fail tests) and assumptions (which cancel tests when conditions aren't met).
The fundamental assertion methods available in all test suites through the Assertions trait.
/**
* Core assertion methods for test verification
*/
trait Assertions {
/**
* Assert that a boolean condition is true
* @param condition the boolean condition to check
* @throws TestFailedException if condition is false
*/
def assert(condition: Boolean): Assertion
/**
* Assert that a boolean condition is true with additional clue
* @param condition the boolean condition to check
* @param clue additional information displayed on failure
*/
def assert(condition: Boolean, clue: Any): Assertion
/**
* Assert that actual value equals expected value
* @param expected the expected value
* @param actual the actual value to compare
*/
def assertResult[T](expected: T)(actual: T): Assertion
/**
* Assert that executing code throws expected exception type
* @param f the code block that should throw
* @return the thrown exception
*/
def assertThrows[T <: AnyRef](f: => Any): T
/**
* Fail the test immediately with default message
*/
def fail(): Nothing
/**
* Fail the test immediately with custom message
* @param message the failure message
*/
def fail(message: String): Nothing
/**
* Cancel the test (different from failure - indicates test couldn't run)
*/
def cancel(): Nothing
/**
* Cancel the test with message
* @param message the cancellation reason
*/
def cancel(message: String): Nothing
/**
* Mark test as pending (not yet implemented)
*/
def pending: Assertion with PendingStatement
/**
* Explicit success assertion (useful in some contexts)
*/
def succeed: Assertion
}Usage Examples:
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.Assertions
class AssertionExamplesSpec extends AnyFunSuite {
test("basic assertions") {
val x = 5
val y = 3
// Simple boolean assertion
assert(x > y)
assert(x > 0, "x should be positive")
// Value comparison
assertResult(8) {
x + y
}
// Exception assertion
assertThrows[ArithmeticException] {
x / 0
}
}
test("test lifecycle control") {
val config = loadTestConfiguration()
if (config.isEmpty) {
cancel("Configuration not available - test cannot run")
}
// Test continues only if config is available
processWithConfig(config.get)
succeed // Explicit success
}
test("pending implementation") {
// Test placeholder for future implementation
pending
}
}Add contextual information to assertion failures using AppendedClues trait.
/**
* Provides withClue method for adding context to assertions
*/
trait AppendedClues {
/**
* Execute assertion with additional clue information
* @param clue contextual information for failure messages
* @param fun assertion code block
*/
def withClue[T](clue: Any)(fun: => T): T
}Usage Example:
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import org.scalatest.AppendedClues
class ClueExamplesSpec extends AnyFunSuite with Matchers with AppendedClues {
test("assertions with contextual clues") {
val users = loadUsers()
val activeUsers = users.filter(_.isActive)
withClue(s"Total users: ${users.size}, Active: ${activeUsers.size}") {
activeUsers.size should be > 0
}
for ((user, index) <- users.zipWithIndex) {
withClue(s"User at index $index: ${user.name}") {
user.email should not be empty
user.age should be >= 0
}
}
}
}Use NonImplicitAssertions when you want to avoid implicit conversions that enable the matcher DSL.
/**
* Assertion methods without implicit conversions to matchers
*/
trait NonImplicitAssertions {
def assert(condition: Boolean): Assertion
def assert(condition: Boolean, clue: Any): Assertion
def assertResult[T](expected: T)(actual: T): Assertion
def assertThrows[T <: AnyRef](f: => Any): T
// ... other assertion methods without matcher support
}Assumptions cancel tests when preconditions aren't met, different from assertions which fail tests.
/**
* Assumption methods for conditional test execution
*/
def assume(condition: Boolean): Assertion
def assume(condition: Boolean, clue: Any): Assertion
def cancel(): Nothing
def cancel(message: String): NothingUsage Example:
import org.scalatest.funsuite.AnyFunSuite
class DatabaseIntegrationSpec extends AnyFunSuite {
test("database operations require connection") {
val dbAvailable = checkDatabaseConnection()
// Cancel test if database isn't available (not a test failure)
assume(dbAvailable, "Database connection not available")
// Test continues only if database is available
val result = performDatabaseOperation()
assert(result.isSuccess)
}
}Safely access contents of common Scala types with helpful error messages.
/**
* Safe access to Option contents
*/
trait OptionValues {
implicit def convertOptionToValuable[T](opt: Option[T]): OptionValuable[T]
final class OptionValuable[T](opt: Option[T]) {
/**
* Get the value from Some, or fail with descriptive message for None
*/
def value: T
}
}
/**
* Safe access to Either contents
*/
trait EitherValues {
implicit def convertEitherToValuable[L, R](either: Either[L, R]): EitherValuable[L, R]
final class EitherValuable[L, R](either: Either[L, R]) {
def left: LeftValuable[L, R]
def right: RightValuable[L, R]
}
final class LeftValuable[L, R](either: Either[L, R]) {
def value: L // Fails if Either is Right
}
final class RightValuable[L, R](either: Either[L, R]) {
def value: R // Fails if Either is Left
}
}
/**
* Safe access to Try contents
*/
trait TryValues {
implicit def convertTryToSuccessOrFailure[T](t: Try[T]): TryValuable[T]
final class TryValuable[T](t: Try[T]) {
def success: SuccessValuable[T]
def failure: FailureValuable
}
final class SuccessValuable[T](t: Try[T]) {
def value: T // Fails if Try is Failure
}
final class FailureValuable(t: Try[_]) {
def exception: Throwable // Fails if Try is Success
}
}
/**
* Safe access to partial function behavior
*/
trait PartialFunctionValues {
implicit def convertPartialFunctionToValuable[A, B](pf: PartialFunction[A, B]): PartialFunctionValuable[A, B]
final class PartialFunctionValuable[A, B](pf: PartialFunction[A, B]) {
/**
* Apply partial function or fail with descriptive message
*/
def valueAt(x: A): B
}
}
/**
* Assert collections contain exactly one element
*/
trait LoneElement {
implicit def convertToCollectionLoneElementWrapper[E, C[_]](collection: C[E])(implicit conv: C[E] => Iterable[E]): CollectionLoneElementWrapper[C]
final class CollectionLoneElementWrapper[C[_]](collection: C[_]) {
/**
* Get the single element, or fail if empty or multiple elements
*/
def loneElement: Any
}
}Usage Examples:
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import org.scalatest.{OptionValues, EitherValues, TryValues, PartialFunctionValues, LoneElement}
import scala.util.{Try, Success, Failure}
class ValueAccessSpec extends AnyFunSuite with Matchers
with OptionValues with EitherValues with TryValues
with PartialFunctionValues with LoneElement {
test("safe Option access") {
val someValue = Some("hello")
val noneValue = None
someValue.value should equal("hello")
// This would fail with descriptive message:
// noneValue.value should equal("anything")
}
test("safe Either access") {
val rightValue: Either[String, Int] = Right(42)
val leftValue: Either[String, Int] = Left("error")
rightValue.right.value should equal(42)
leftValue.left.value should equal("error")
// These would fail with descriptive messages:
// rightValue.left.value
// leftValue.right.value
}
test("safe Try access") {
val success: Try[Int] = Success(42)
val failure: Try[Int] = Failure(new RuntimeException("oops"))
success.success.value should equal(42)
failure.failure.exception.getMessage should equal("oops")
}
test("partial function testing") {
val pf: PartialFunction[Int, String] = {
case x if x > 0 => s"positive: $x"
}
pf.valueAt(5) should equal("positive: 5")
// This would fail with descriptive message:
// pf.valueAt(-1) should equal("anything")
}
test("lone element access") {
val singleItem = List("only")
val multipleItems = List("first", "second")
val emptyList = List.empty[String]
singleItem.loneElement should equal("only")
// These would fail with descriptive messages:
// multipleItems.loneElement
// emptyList.loneElement
}
}Control how exceptions and stack traces are handled in test failures.
/**
* Control stack trace display in test failures
*/
trait SeveredStackTraces {
// Automatically shortens stack traces to focus on relevant test code
}
/**
* Handle exceptions during test reporting
*/
trait CatchReporter {
// Catches and reports exceptions that occur during test reporting
}Create reusable assertion helpers for domain-specific testing.
Usage Example:
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import org.scalatest.Assertion
class CustomAssertionSpec extends AnyFunSuite with Matchers {
// Custom assertion helper
def assertValidEmail(email: String): Assertion = {
withClue(s"Invalid email format: '$email'") {
assert(email.contains("@"), "Email must contain @")
assert(email.contains("."), "Email must contain domain")
assert(!email.startsWith("@"), "Email cannot start with @")
assert(!email.endsWith("@"), "Email cannot end with @")
}
}
// Another custom helper
def assertBetween[T](value: T, min: T, max: T)(implicit ord: Ordering[T]): Assertion = {
withClue(s"Value $value not between $min and $max") {
assert(ord.gteq(value, min) && ord.lteq(value, max))
}
}
test("custom assertions in action") {
assertValidEmail("user@example.com")
assertBetween(5, 1, 10)
assertBetween(2.5, 2.0, 3.0)
// These would fail:
// assertValidEmail("invalid-email")
// assertBetween(15, 1, 10)
}
}// Test that specific exception is thrown
assertThrows[IllegalArgumentException] {
new BankAccount(-100) // negative balance
}
// Capture and inspect the exception
val exception = assertThrows[ValidationException] {
validator.validate(invalidData)
}
exception.getMessage should include("required field")
exception.getFieldName should equal("email")// Use assumptions for environment-dependent tests
assume(System.getProperty("env") == "integration", "Integration tests only")
// Use cancellation for unavailable resources
if (!externalServiceAvailable) {
cancel("External service unavailable")
}// Multiple related assertions with context
withClue("User validation failed") {
user.name should not be empty
user.email should include("@")
user.age should be >= 18
}