or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-specifications.mddsl-components.mdindex.mdintegration-features.mdmatcher-system.mdmutable-specifications.mdreporting.mdtest-execution.md
tile.json

mutable-specifications.mddocs/

Mutable Specifications

Mutable specifications provide an imperative-style API for writing tests where examples are defined using side-effects and thrown expectations. This style is familiar to developers coming from traditional unit testing frameworks like JUnit or ScalaTest.

Mutable Specification Classes

Specification (mutable)

Main mutable specification class using thrown expectations.

// org.specs2.mutable
abstract class Specification extends SpecificationLike

Usage Example:

import org.specs2.mutable._

class CalculatorSpec extends Specification {
  "Calculator" should {
    "add two numbers" in {
      val calc = new Calculator
      calc.add(2, 3) must beEqualTo(5)
    }
    
    "subtract two numbers" in {
      val calc = new Calculator  
      calc.subtract(5, 2) must beEqualTo(3)
    }
    
    "handle division by zero" in {
      val calc = new Calculator
      calc.divide(1, 0) must throwA[ArithmeticException]
    }
  }
}

SpecificationLike (mutable)

Core mutable specification trait.

trait SpecificationLike extends SpecificationStructure 
  with SpecificationCreation 
  with SpecificationFeatures

Provides mutable-specific implementations of:

  • Example creation with side-effects
  • Thrown expectations on failures
  • Imperative specification building

Spec (mutable)

Lightweight mutable specification class.

abstract class Spec extends SpecLike

Usage Example:

import org.specs2.mutable._

class SimpleSpec extends Spec {
  "Simple test" should {
    "pass" in {
      1 + 1 must beEqualTo(2)
    }
  }
}

SpecLike (mutable)

Lightweight mutable specification trait.

trait SpecLike extends SpecificationStructure 
  with ExampleDsl0 
  with MustThrownMatchers1

Includes:

  • ExampleDsl0: Basic example creation methods
  • MustThrownMatchers1: Core matchers with thrown expectations

Mutable DSL Components

ExampleDsl (mutable)

DSL for creating examples in mutable specifications.

trait ExampleDsl {
  def in[T: AsResult](body: => T): Unit
  def should[T: AsResult](body: => T): Unit  
  def can[T: AsResult](body: => T): Unit
  def >>[T: AsResult](body: => T): Unit
}

TextDsl (mutable)

Adding descriptive text to mutable specifications.

trait TextDsl {
  def br: Unit
  def p: Unit  
  def tab: Unit
  def backtab: Unit
  def end: Unit
}

MutableFragmentBuilder

Building specification structure imperatively.

trait MutableFragmentBuilder {
  def addFragment(fragment: Fragment): Unit
  def addText(text: String): Unit
  def addExample(description: String, body: => Any): Unit
  def addStep(action: => Any): Unit
}

Thrown Expectations

MustThrownMatchers

Matchers that throw exceptions on failure (for mutable specs).

trait MustThrownMatchers extends MustMatchers {
  override def createExpectation[T](t: => T): Expectable[T]
  override def checkFailure[T](m: MatchResult[T]): MatchResult[T]
}

ShouldThrownMatchers

Alternative "should" syntax with thrown expectations.

trait ShouldThrownMatchers extends ShouldMatchers {
  override def createExpectation[T](t: => T): Expectable[T]
  override def checkFailure[T](m: MatchResult[T]): MatchResult[T]  
}

Mutable Specification Structure

SpecificationStructure (mutable)

Mutable specification structure management.

trait SpecificationStructure {
  protected var specFragments: Vector[Fragment] = Vector.empty
  
  def addFragment(f: Fragment): Unit
  def prependFragment(f: Fragment): Unit
  def content: Fragments
}

MutableSpecificationStructure

Enhanced mutable structure with modification methods.

trait MutableSpecificationStructure extends SpecificationStructure {
  def insertFragment(index: Int, f: Fragment): Unit
  def removeFragment(index: Int): Unit
  def replaceFragment(index: Int, f: Fragment): Unit
  def clearFragments(): Unit
}

Context Support

BeforeEach (mutable)

Setup actions before each example.

trait BeforeEach {
  def before: Any
  
  def apply[T: AsResult](a: => T): Result = {
    before
    AsResult(a)
  }
}

AfterEach (mutable)

Cleanup actions after each example.

trait AfterEach {
  def after: Any
  
  def apply[T: AsResult](a: => T): Result = {
    try AsResult(a)
    finally after
  }
}

AroundEach (mutable)

Wrap each example with custom logic.

trait AroundEach {
  def around[T: AsResult](t: => T): Result
  
  def apply[T: AsResult](a: => T): Result = around(a)
}

BeforeAfterEach

Combined setup and cleanup.

trait BeforeAfterEach extends BeforeEach with AfterEach {
  def apply[T: AsResult](a: => T): Result = {
    before
    try AsResult(a)
    finally after
  }
}

Advanced Mutable Features

Specification Scoping

Nested specification structure:

class DatabaseSpec extends Specification {
  "Database operations" should {
    "User operations" should {
      "create users" in { /* test */ }
      "update users" in { /* test */ }
      "delete users" in { /* test */ }
    }
    
    "Order operations" should {
      "create orders" in { /* test */ }
      "process orders" in { /* test */ }
    }
  }
}

Specification Arguments (mutable)

Configure mutable specification behavior:

trait ArgumentsShortcuts {
  def sequential: Arguments
  def isolated: Arguments  
  def stopOnFail: Arguments
  def plan: Arguments
  def skipAll: Arguments
}

Tags and Sections (mutable)

Organizing and filtering examples:

trait TagDsl {
  def tag(names: String*): Unit
  def section(name: String): Unit
}

Usage:

class TaggedSpec extends Specification {
  "Feature X" should {
    "work in normal cases" in {
      // test code
    } tag("unit", "fast")
    
    "work under load" in {
      // test code  
    } tag("integration", "slow")
  }
}

Usage Patterns

Basic Mutable Pattern

import org.specs2.mutable._

class UserServiceSpec extends Specification {
  "UserService" should {
    val service = new UserService
    
    "create new users" in {
      val user = service.create("john", "john@test.com")
      user.name must beEqualTo("john")
      user.email must beEqualTo("john@test.com")
    }
    
    "find users by email" in {
      service.create("jane", "jane@test.com")
      val found = service.findByEmail("jane@test.com")
      found must beSome.which(_.name == "jane")
    }
  }
}

Setup and Teardown

class DatabaseSpec extends Specification with BeforeAfterEach {
  def before = {
    Database.createTables()
    Database.seedTestData()
  }
  
  def after = {
    Database.cleanup()
  }
  
  "Database operations" should {
    "insert records" in {
      val count = Database.insert(testRecord)
      count must beEqualTo(1)
    }
    
    "query records" in {
      val results = Database.query("SELECT * FROM users")
      results must not(beEmpty)
    }
  }
}

Shared Examples

trait UserBehavior {
  def validUser = {
    "have a valid name" in {
      user.name must not(beEmpty)
    }
    
    "have a valid email" in {
      user.email must beMatching(emailRegex)
    }
  }
  
  def user: User
  def emailRegex: String
}

class UserSpec extends Specification with UserBehavior {
  def user = User("john", "john@test.com")
  def emailRegex = ".*@.*\\..*"
  
  "User" should {
    validUser
    
    "calculate age correctly" in {
      user.copy(birthYear = 1990).age must beGreaterThan(30)
    }
  }
}

Conditional Examples

class ConditionalSpec extends Specification {
  "Feature" should {
    if (System.getProperty("integration.tests") == "true") {
      "work with external service" in {
        // integration test code
      }
    }
    
    "work offline" in {
      // unit test code
    }
  }
}

Best Practices

  1. Use descriptive nesting: Create logical hierarchies with nested "should" blocks
  2. Keep setup minimal: Use context traits for complex setup/teardown scenarios
  3. Group related tests: Use nested structure to group related functionality
  4. Avoid shared mutable state: Each example should be independent
  5. Use tags wisely: Tag examples for filtering slow or integration tests
  6. Handle exceptions properly: Use appropriate matchers for expected exceptions
  7. Document with text: Add descriptive text blocks to explain complex scenarios