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.
Full acceptance testing DSL combining all DSL components.
trait AcceptanceDsl extends FragmentsDsl
with SpecStructureDsl
with TitleDsl
with ExampleDsl
with ReferenceDsl
with TagDsl
with ActionDslProvides complete DSL functionality for immutable specifications.
Lightweight acceptance DSL with essential components only.
trait AcceptanceDsl1 extends FragmentsDsl1
with ExampleDsl1
with TitleDsl1Minimal subset for basic specifications with reduced imports.
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)
}Minimal example DSL with core methods.
trait ExampleDsl1 {
def in[T: AsResult](body: => T): Fragment
def should[T: AsResult](body: => T): Fragment
}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 breakp: Paragraph breakend: End specification processingt(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
}Minimal fragment DSL.
trait FragmentsDsl1 {
def br: Fragment
def p: Fragment
}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
"""Minimal title DSL.
trait TitleDsl1 {
def title(t: String): Fragment
}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
"""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")}
"""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"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())}
"""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)}
"""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)}
"""Minimal string interpolation.
trait S2StringContext1 {
implicit class S2StringContext1(sc: StringContext) {
def s2(args: Any*): Fragments
}
}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)) }}
"""
}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)
}}
"""
}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())}
"""
}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
}
}AcceptanceDsl) and minimal (AcceptanceDsl1) based on needsbr, p, t, bt for readable specification layoutsection and group for logical organizationstep and action for setup/teardown at appropriate phases