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

dsl-components.mddocs/

DSL Components

Specs2 provides a rich domain-specific language (DSL) for creating readable and expressive test specifications. The DSL components enable natural language-like test definitions with powerful composition and structuring capabilities.

Core DSL Traits

AcceptanceDsl

Full acceptance testing DSL combining all DSL components.

trait AcceptanceDsl extends FragmentsDsl 
  with SpecStructureDsl 
  with TitleDsl 
  with ExampleDsl 
  with ReferenceDsl 
  with TagDsl 
  with ActionDsl

Provides complete DSL functionality for immutable specifications.

AcceptanceDsl1

Lightweight acceptance DSL with essential components only.

trait AcceptanceDsl1 extends FragmentsDsl1 
  with ExampleDsl1 
  with TitleDsl1

Minimal subset for basic specifications with reduced imports.

Example Creation DSL

ExampleDsl

DSL for creating test examples with various syntax options.

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

Usage Examples:

class ExampleDslSpec extends Specification { def is = s2"""
  Calculator should
    add two numbers correctly    ${ 2 + 3 must beEqualTo(5) }
    handle division by zero      ${ 10 / 0 must throwA[ArithmeticException] }
    
  Advanced operations
    calculate square root        $sqrt
    compute factorial           $factorial
    parse expressions           $todo
  """
  
  def sqrt = Math.sqrt(16) must beEqualTo(4.0)
  def factorial = factorial(5) must beEqualTo(120)
}

ExampleDsl1

Minimal example DSL with core methods.

trait ExampleDsl1 {
  def in[T: AsResult](body: => T): Fragment
  def should[T: AsResult](body: => T): Fragment
}

Fragment Management DSL

FragmentsDsl

DSL for building and organizing specification fragments.

trait FragmentsDsl {
  def br: Fragment
  def p: Fragment
  def end: Fragment
  def t: Fragment
  def t(n: Int): Fragment
  def bt: Fragment
  def bt(n: Int): Fragment
}

Fragment Types:

  • br: Line break
  • p: Paragraph break
  • end: End specification processing
  • t(n): Tab indentation (n levels)
  • bt(n): Back-tab (reduce indentation)

Usage Example:

class FormattedSpec extends Specification { def is = s2"""
  Main section
  ${br}
    Subsection with indentation
    ${t}example 1    $e1
    ${t}example 2    $e2
  ${bt}
  Back to main level
    example 3        $e3
  """
  
  def e1 = success
  def e2 = success  
  def e3 = success
}

FragmentsDsl1

Minimal fragment DSL.

trait FragmentsDsl1 {
  def br: Fragment
  def p: Fragment
}

Title and Structure DSL

TitleDsl

DSL for creating specification titles and section headers.

trait TitleDsl {
  def title(t: String): Fragment
  def section(name: String): Fragment
  def group(name: String): Fragment
}

Usage Example:

class TitledSpec extends Specification { def is = 
  title("User Management System") ^
  section("User Creation") ^ s2"""
    Creating users should
      validate required fields     $validateRequired
      generate unique IDs          $generateIds
    """ ^
  section("User Authentication") ^ s2"""
    Authentication should  
      verify passwords             $verifyPasswords
      handle invalid credentials   $handleInvalid
    """

TitleDsl1

Minimal title DSL.

trait TitleDsl1 {
  def title(t: String): Fragment
}

Specification Structure DSL

SpecStructureDsl

DSL for organizing specification structure and flow control.

trait SpecStructureDsl {
  def sequential: Fragment
  def isolated: Fragment
  def stopOnFail: Fragment
  def skipAll: Fragment
  def plan: Fragment
  def args(arguments: Arguments): Fragment
}

Usage Example:

class StructuredSpec extends Specification { def is = 
  args(sequential = true, stopOnFail = true) ^ s2"""
  Integration tests (run sequentially)
    setup database        $setupDb
    create test data      $createData  
    run main tests        $runTests
    cleanup              $cleanup
  """

Tagging and Categorization DSL

TagDsl

DSL for tagging examples and sections for organization and filtering.

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

Usage Example:

class TaggedSpec extends Specification { def is = s2"""
  User service tests
    create user (fast test)           $createUser
    ${tag("unit", "fast")}
    
    bulk import users (slow test)     $bulkImport  
    ${tag("integration", "slow")}
    
    load test with 1000 users         $loadTest
    ${tag("performance", "slow", "manual")}
  """

Filtering by Tags

Run specific tags:

# Run only fast tests
testOnly *Spec -- include "fast"

# Exclude slow tests  
testOnly *Spec -- exclude "slow"

# Run unit tests only
testOnly *Spec -- include "unit" exclude "integration"

Action and Step DSL

ActionDsl

DSL for setup, teardown, and step actions.

trait ActionDsl {
  def step(action: => Any): Fragment
  def action(action: => Any): Fragment
}

Usage Example:

class ActionSpec extends Specification { def is = s2"""
  Database operations
    ${step(Database.createTables())}
    insert records               $insertRecords
    ${step(Database.seedTestData())}
    query records               $queryRecords
    update records              $updateRecords  
    ${step(Database.cleanup())}
  """

Step vs Action

  • Step: Executed during specification building phase
  • Action: Executed during example execution phase

Reference DSL

ReferenceDsl

DSL for including other specifications and creating references.

trait ReferenceDsl {
  def include(spec: SpecificationStructure): Fragment
  def link(spec: SpecificationStructure): Fragment
  def see(spec: SpecificationStructure): Fragment
}

Usage Example:

class MainSpec extends Specification { def is = s2"""
  Complete test suite
    ${include(new UserSpec)}
    ${include(new OrderSpec)}
    ${link(new PerformanceSpec)}
  """

String Context DSL

S2StringContext

Advanced string interpolation for specifications.

trait S2StringContext {
  implicit class S2StringContext(sc: StringContext) {
    def s2(args: Any*): Fragments
  }
}

Usage Patterns:

class InterpolatedSpec extends Specification { def is = s2"""
  String interpolation examples
    example with variable        ${variable must beEqualTo(expected)}
    example with method call     ${method() must beEqualTo(result)}
    example with complex expr    ${complexCalculation must satisfy(predicate)}
  """

S2StringContext1

Minimal string interpolation.

trait S2StringContext1 {
  implicit class S2StringContext1(sc: StringContext) {
    def s2(args: Any*): Fragments
  }
}

Advanced DSL Features

Auto Examples

Automatically capture code as examples:

trait AutoExamples {
  def eg[T: AsResult](code: T): Fragment
}

Usage:

class AutoExampleSpec extends Specification with AutoExamples { def is = s2"""
  Auto-captured examples
    ${eg { calculator.add(2, 3) must beEqualTo(5) }}
    ${eg { list.filter(_ > 0) must contain(exactly(1, 2, 3)) }}
  """
}

Snippets

Extract and display code snippets in specifications:

trait Snippets {
  def snippet[T](code: => T): Fragment
  def eval[T](code: => T): Fragment
}

Usage:

class SnippetSpec extends Specification with Snippets { def is = s2"""
  Code examples
    Basic usage:
    ${snippet {
      val user = User("john", "john@test.com")
      user.isValid must beTrue
    }}
    
    Advanced usage:
    ${eval {
      val users = loadUsers()
      users.filter(_.active) must haveSize(expectedCount)
    }}
  """
}

DSL Composition Patterns

Combining DSL Traits

Create custom DSL combinations:

trait MyCustomDsl extends AcceptanceDsl1 
  with TagDsl 
  with ActionDsl {
  
  // Custom DSL methods
  def setup(action: => Any) = step(action)
  def cleanup(action: => Any) = step(action)
  
  def fastTest[T: AsResult](body: => T) = 
    (body must not(throwAn[Exception])) ^ tag("fast")
}

class CustomSpec extends Specification with MyCustomDsl { def is = s2"""
  Custom DSL example
    ${setup(initializeDatabase())}
    fast operation test    ${fastTest { quickOperation() }}
    ${cleanup(shutdownDatabase())}
  """
}

Specification Templates

Create reusable specification templates:

trait ServiceSpecTemplate extends Specification {
  def serviceName: String
  def service: Any
  def validInput: Any
  def invalidInput: Any
  
  def is = s2"""
  $serviceName service should
    handle valid input     $validCase
    reject invalid input   $invalidCase
    be thread-safe         $threadSafeCase
  """
  
  def validCase: Result
  def invalidCase: Result  
  def threadSafeCase: Result
}

class UserServiceSpec extends ServiceSpecTemplate {
  def serviceName = "User"
  def service = new UserService
  def validInput = User("john", "john@test.com")
  def invalidInput = User("", "invalid-email")
  
  def validCase = service.create(validInput) must beSuccessful
  def invalidCase = service.create(invalidInput) must beFailure
  def threadSafeCase = {
    val futures = (1 to 100).map(_ => Future(service.getCount))
    Future.sequence(futures) must not(throwAn[Exception]).await
  }
}

Best Practices

  1. Use appropriate DSL level: Choose between full (AcceptanceDsl) and minimal (AcceptanceDsl1) based on needs
  2. Structure with fragments: Use br, p, t, bt for readable specification layout
  3. Tag strategically: Use tags for test categorization and filtering
  4. Compose DSL traits: Create custom DSL combinations for domain-specific needs
  5. Leverage string interpolation: Use s2 strings for embedding examples directly
  6. Organize with sections: Use section and group for logical organization
  7. Use steps wisely: Apply step and action for setup/teardown at appropriate phases
  8. Template common patterns: Create reusable specification templates for similar test structures