0
# DSL Components
1
2
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.
3
4
## Core DSL Traits
5
6
### AcceptanceDsl
7
8
Full acceptance testing DSL combining all DSL components.
9
10
```scala { .api }
11
trait AcceptanceDsl extends FragmentsDsl
12
with SpecStructureDsl
13
with TitleDsl
14
with ExampleDsl
15
with ReferenceDsl
16
with TagDsl
17
with ActionDsl
18
```
19
20
Provides complete DSL functionality for immutable specifications.
21
22
### AcceptanceDsl1
23
24
Lightweight acceptance DSL with essential components only.
25
26
```scala { .api }
27
trait AcceptanceDsl1 extends FragmentsDsl1
28
with ExampleDsl1
29
with TitleDsl1
30
```
31
32
Minimal subset for basic specifications with reduced imports.
33
34
## Example Creation DSL
35
36
### ExampleDsl
37
38
DSL for creating test examples with various syntax options.
39
40
```scala { .api }
41
trait ExampleDsl {
42
def in[T: AsResult](body: => T): Fragment
43
def >>[T: AsResult](body: => T): Fragment
44
def should[T: AsResult](body: => T): Fragment
45
def can[T: AsResult](body: => T): Fragment
46
def todo: Fragment
47
def done: Fragment
48
}
49
```
50
51
**Usage Examples:**
52
```scala
53
class ExampleDslSpec extends Specification { def is = s2"""
54
Calculator should
55
add two numbers correctly ${ 2 + 3 must beEqualTo(5) }
56
handle division by zero ${ 10 / 0 must throwA[ArithmeticException] }
57
58
Advanced operations
59
calculate square root $sqrt
60
compute factorial $factorial
61
parse expressions $todo
62
"""
63
64
def sqrt = Math.sqrt(16) must beEqualTo(4.0)
65
def factorial = factorial(5) must beEqualTo(120)
66
}
67
```
68
69
### ExampleDsl1
70
71
Minimal example DSL with core methods.
72
73
```scala { .api }
74
trait ExampleDsl1 {
75
def in[T: AsResult](body: => T): Fragment
76
def should[T: AsResult](body: => T): Fragment
77
}
78
```
79
80
## Fragment Management DSL
81
82
### FragmentsDsl
83
84
DSL for building and organizing specification fragments.
85
86
```scala { .api }
87
trait FragmentsDsl {
88
def br: Fragment
89
def p: Fragment
90
def end: Fragment
91
def t: Fragment
92
def t(n: Int): Fragment
93
def bt: Fragment
94
def bt(n: Int): Fragment
95
}
96
```
97
98
**Fragment Types:**
99
- `br`: Line break
100
- `p`: Paragraph break
101
- `end`: End specification processing
102
- `t(n)`: Tab indentation (n levels)
103
- `bt(n)`: Back-tab (reduce indentation)
104
105
**Usage Example:**
106
```scala
107
class FormattedSpec extends Specification { def is = s2"""
108
Main section
109
${br}
110
Subsection with indentation
111
${t}example 1 $e1
112
${t}example 2 $e2
113
${bt}
114
Back to main level
115
example 3 $e3
116
"""
117
118
def e1 = success
119
def e2 = success
120
def e3 = success
121
}
122
```
123
124
### FragmentsDsl1
125
126
Minimal fragment DSL.
127
128
```scala { .api }
129
trait FragmentsDsl1 {
130
def br: Fragment
131
def p: Fragment
132
}
133
```
134
135
## Title and Structure DSL
136
137
### TitleDsl
138
139
DSL for creating specification titles and section headers.
140
141
```scala { .api }
142
trait TitleDsl {
143
def title(t: String): Fragment
144
def section(name: String): Fragment
145
def group(name: String): Fragment
146
}
147
```
148
149
**Usage Example:**
150
```scala
151
class TitledSpec extends Specification { def is =
152
title("User Management System") ^
153
section("User Creation") ^ s2"""
154
Creating users should
155
validate required fields $validateRequired
156
generate unique IDs $generateIds
157
""" ^
158
section("User Authentication") ^ s2"""
159
Authentication should
160
verify passwords $verifyPasswords
161
handle invalid credentials $handleInvalid
162
"""
163
```
164
165
### TitleDsl1
166
167
Minimal title DSL.
168
169
```scala { .api }
170
trait TitleDsl1 {
171
def title(t: String): Fragment
172
}
173
```
174
175
## Specification Structure DSL
176
177
### SpecStructureDsl
178
179
DSL for organizing specification structure and flow control.
180
181
```scala { .api }
182
trait SpecStructureDsl {
183
def sequential: Fragment
184
def isolated: Fragment
185
def stopOnFail: Fragment
186
def skipAll: Fragment
187
def plan: Fragment
188
def args(arguments: Arguments): Fragment
189
}
190
```
191
192
**Usage Example:**
193
```scala
194
class StructuredSpec extends Specification { def is =
195
args(sequential = true, stopOnFail = true) ^ s2"""
196
Integration tests (run sequentially)
197
setup database $setupDb
198
create test data $createData
199
run main tests $runTests
200
cleanup $cleanup
201
"""
202
```
203
204
## Tagging and Categorization DSL
205
206
### TagDsl
207
208
DSL for tagging examples and sections for organization and filtering.
209
210
```scala { .api }
211
trait TagDsl {
212
def tag(names: String*): Fragment
213
def section(name: String): Fragment
214
def group(name: String): Fragment
215
}
216
```
217
218
**Usage Example:**
219
```scala
220
class TaggedSpec extends Specification { def is = s2"""
221
User service tests
222
create user (fast test) $createUser
223
${tag("unit", "fast")}
224
225
bulk import users (slow test) $bulkImport
226
${tag("integration", "slow")}
227
228
load test with 1000 users $loadTest
229
${tag("performance", "slow", "manual")}
230
"""
231
```
232
233
### Filtering by Tags
234
235
Run specific tags:
236
```bash
237
# Run only fast tests
238
testOnly *Spec -- include "fast"
239
240
# Exclude slow tests
241
testOnly *Spec -- exclude "slow"
242
243
# Run unit tests only
244
testOnly *Spec -- include "unit" exclude "integration"
245
```
246
247
## Action and Step DSL
248
249
### ActionDsl
250
251
DSL for setup, teardown, and step actions.
252
253
```scala { .api }
254
trait ActionDsl {
255
def step(action: => Any): Fragment
256
def action(action: => Any): Fragment
257
}
258
```
259
260
**Usage Example:**
261
```scala
262
class ActionSpec extends Specification { def is = s2"""
263
Database operations
264
${step(Database.createTables())}
265
insert records $insertRecords
266
${step(Database.seedTestData())}
267
query records $queryRecords
268
update records $updateRecords
269
${step(Database.cleanup())}
270
"""
271
```
272
273
### Step vs Action
274
275
- **Step**: Executed during specification building phase
276
- **Action**: Executed during example execution phase
277
278
## Reference DSL
279
280
### ReferenceDsl
281
282
DSL for including other specifications and creating references.
283
284
```scala { .api }
285
trait ReferenceDsl {
286
def include(spec: SpecificationStructure): Fragment
287
def link(spec: SpecificationStructure): Fragment
288
def see(spec: SpecificationStructure): Fragment
289
}
290
```
291
292
**Usage Example:**
293
```scala
294
class MainSpec extends Specification { def is = s2"""
295
Complete test suite
296
${include(new UserSpec)}
297
${include(new OrderSpec)}
298
${link(new PerformanceSpec)}
299
"""
300
```
301
302
## String Context DSL
303
304
### S2StringContext
305
306
Advanced string interpolation for specifications.
307
308
```scala { .api }
309
trait S2StringContext {
310
implicit class S2StringContext(sc: StringContext) {
311
def s2(args: Any*): Fragments
312
}
313
}
314
```
315
316
**Usage Patterns:**
317
```scala
318
class InterpolatedSpec extends Specification { def is = s2"""
319
String interpolation examples
320
example with variable ${variable must beEqualTo(expected)}
321
example with method call ${method() must beEqualTo(result)}
322
example with complex expr ${complexCalculation must satisfy(predicate)}
323
"""
324
```
325
326
### S2StringContext1
327
328
Minimal string interpolation.
329
330
```scala { .api }
331
trait S2StringContext1 {
332
implicit class S2StringContext1(sc: StringContext) {
333
def s2(args: Any*): Fragments
334
}
335
}
336
```
337
338
## Advanced DSL Features
339
340
### Auto Examples
341
342
Automatically capture code as examples:
343
344
```scala { .api }
345
trait AutoExamples {
346
def eg[T: AsResult](code: T): Fragment
347
}
348
```
349
350
**Usage:**
351
```scala
352
class AutoExampleSpec extends Specification with AutoExamples { def is = s2"""
353
Auto-captured examples
354
${eg { calculator.add(2, 3) must beEqualTo(5) }}
355
${eg { list.filter(_ > 0) must contain(exactly(1, 2, 3)) }}
356
"""
357
}
358
```
359
360
### Snippets
361
362
Extract and display code snippets in specifications:
363
364
```scala { .api }
365
trait Snippets {
366
def snippet[T](code: => T): Fragment
367
def eval[T](code: => T): Fragment
368
}
369
```
370
371
**Usage:**
372
```scala
373
class SnippetSpec extends Specification with Snippets { def is = s2"""
374
Code examples
375
Basic usage:
376
${snippet {
377
val user = User("john", "john@test.com")
378
user.isValid must beTrue
379
}}
380
381
Advanced usage:
382
${eval {
383
val users = loadUsers()
384
users.filter(_.active) must haveSize(expectedCount)
385
}}
386
"""
387
}
388
```
389
390
## DSL Composition Patterns
391
392
### Combining DSL Traits
393
394
Create custom DSL combinations:
395
396
```scala
397
trait MyCustomDsl extends AcceptanceDsl1
398
with TagDsl
399
with ActionDsl {
400
401
// Custom DSL methods
402
def setup(action: => Any) = step(action)
403
def cleanup(action: => Any) = step(action)
404
405
def fastTest[T: AsResult](body: => T) =
406
(body must not(throwAn[Exception])) ^ tag("fast")
407
}
408
409
class CustomSpec extends Specification with MyCustomDsl { def is = s2"""
410
Custom DSL example
411
${setup(initializeDatabase())}
412
fast operation test ${fastTest { quickOperation() }}
413
${cleanup(shutdownDatabase())}
414
"""
415
}
416
```
417
418
### Specification Templates
419
420
Create reusable specification templates:
421
422
```scala
423
trait ServiceSpecTemplate extends Specification {
424
def serviceName: String
425
def service: Any
426
def validInput: Any
427
def invalidInput: Any
428
429
def is = s2"""
430
$serviceName service should
431
handle valid input $validCase
432
reject invalid input $invalidCase
433
be thread-safe $threadSafeCase
434
"""
435
436
def validCase: Result
437
def invalidCase: Result
438
def threadSafeCase: Result
439
}
440
441
class UserServiceSpec extends ServiceSpecTemplate {
442
def serviceName = "User"
443
def service = new UserService
444
def validInput = User("john", "john@test.com")
445
def invalidInput = User("", "invalid-email")
446
447
def validCase = service.create(validInput) must beSuccessful
448
def invalidCase = service.create(invalidInput) must beFailure
449
def threadSafeCase = {
450
val futures = (1 to 100).map(_ => Future(service.getCount))
451
Future.sequence(futures) must not(throwAn[Exception]).await
452
}
453
}
454
```
455
456
## Best Practices
457
458
1. **Use appropriate DSL level**: Choose between full (`AcceptanceDsl`) and minimal (`AcceptanceDsl1`) based on needs
459
2. **Structure with fragments**: Use `br`, `p`, `t`, `bt` for readable specification layout
460
3. **Tag strategically**: Use tags for test categorization and filtering
461
4. **Compose DSL traits**: Create custom DSL combinations for domain-specific needs
462
5. **Leverage string interpolation**: Use s2 strings for embedding examples directly
463
6. **Organize with sections**: Use `section` and `group` for logical organization
464
7. **Use steps wisely**: Apply `step` and `action` for setup/teardown at appropriate phases
465
8. **Template common patterns**: Create reusable specification templates for similar test structures