0
# Mutable Specifications
1
2
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.
3
4
## Mutable Specification Classes
5
6
### Specification (mutable)
7
8
Main mutable specification class using thrown expectations.
9
10
```scala { .api }
11
// org.specs2.mutable
12
abstract class Specification extends SpecificationLike
13
```
14
15
**Usage Example:**
16
```scala
17
import org.specs2.mutable._
18
19
class CalculatorSpec extends Specification {
20
"Calculator" should {
21
"add two numbers" in {
22
val calc = new Calculator
23
calc.add(2, 3) must beEqualTo(5)
24
}
25
26
"subtract two numbers" in {
27
val calc = new Calculator
28
calc.subtract(5, 2) must beEqualTo(3)
29
}
30
31
"handle division by zero" in {
32
val calc = new Calculator
33
calc.divide(1, 0) must throwA[ArithmeticException]
34
}
35
}
36
}
37
```
38
39
### SpecificationLike (mutable)
40
41
Core mutable specification trait.
42
43
```scala { .api }
44
trait SpecificationLike extends SpecificationStructure
45
with SpecificationCreation
46
with SpecificationFeatures
47
```
48
49
Provides mutable-specific implementations of:
50
- Example creation with side-effects
51
- Thrown expectations on failures
52
- Imperative specification building
53
54
### Spec (mutable)
55
56
Lightweight mutable specification class.
57
58
```scala { .api }
59
abstract class Spec extends SpecLike
60
```
61
62
**Usage Example:**
63
```scala
64
import org.specs2.mutable._
65
66
class SimpleSpec extends Spec {
67
"Simple test" should {
68
"pass" in {
69
1 + 1 must beEqualTo(2)
70
}
71
}
72
}
73
```
74
75
### SpecLike (mutable)
76
77
Lightweight mutable specification trait.
78
79
```scala { .api }
80
trait SpecLike extends SpecificationStructure
81
with ExampleDsl0
82
with MustThrownMatchers1
83
```
84
85
Includes:
86
- `ExampleDsl0`: Basic example creation methods
87
- `MustThrownMatchers1`: Core matchers with thrown expectations
88
89
## Mutable DSL Components
90
91
### ExampleDsl (mutable)
92
93
DSL for creating examples in mutable specifications.
94
95
```scala { .api }
96
trait ExampleDsl {
97
def in[T: AsResult](body: => T): Unit
98
def should[T: AsResult](body: => T): Unit
99
def can[T: AsResult](body: => T): Unit
100
def >>[T: AsResult](body: => T): Unit
101
}
102
```
103
104
### TextDsl (mutable)
105
106
Adding descriptive text to mutable specifications.
107
108
```scala { .api }
109
trait TextDsl {
110
def br: Unit
111
def p: Unit
112
def tab: Unit
113
def backtab: Unit
114
def end: Unit
115
}
116
```
117
118
### MutableFragmentBuilder
119
120
Building specification structure imperatively.
121
122
```scala { .api }
123
trait MutableFragmentBuilder {
124
def addFragment(fragment: Fragment): Unit
125
def addText(text: String): Unit
126
def addExample(description: String, body: => Any): Unit
127
def addStep(action: => Any): Unit
128
}
129
```
130
131
## Thrown Expectations
132
133
### MustThrownMatchers
134
135
Matchers that throw exceptions on failure (for mutable specs).
136
137
```scala { .api }
138
trait MustThrownMatchers extends MustMatchers {
139
override def createExpectation[T](t: => T): Expectable[T]
140
override def checkFailure[T](m: MatchResult[T]): MatchResult[T]
141
}
142
```
143
144
### ShouldThrownMatchers
145
146
Alternative "should" syntax with thrown expectations.
147
148
```scala { .api }
149
trait ShouldThrownMatchers extends ShouldMatchers {
150
override def createExpectation[T](t: => T): Expectable[T]
151
override def checkFailure[T](m: MatchResult[T]): MatchResult[T]
152
}
153
```
154
155
## Mutable Specification Structure
156
157
### SpecificationStructure (mutable)
158
159
Mutable specification structure management.
160
161
```scala { .api }
162
trait SpecificationStructure {
163
protected var specFragments: Vector[Fragment] = Vector.empty
164
165
def addFragment(f: Fragment): Unit
166
def prependFragment(f: Fragment): Unit
167
def content: Fragments
168
}
169
```
170
171
### MutableSpecificationStructure
172
173
Enhanced mutable structure with modification methods.
174
175
```scala { .api }
176
trait MutableSpecificationStructure extends SpecificationStructure {
177
def insertFragment(index: Int, f: Fragment): Unit
178
def removeFragment(index: Int): Unit
179
def replaceFragment(index: Int, f: Fragment): Unit
180
def clearFragments(): Unit
181
}
182
```
183
184
## Context Support
185
186
### BeforeEach (mutable)
187
188
Setup actions before each example.
189
190
```scala { .api }
191
trait BeforeEach {
192
def before: Any
193
194
def apply[T: AsResult](a: => T): Result = {
195
before
196
AsResult(a)
197
}
198
}
199
```
200
201
### AfterEach (mutable)
202
203
Cleanup actions after each example.
204
205
```scala { .api }
206
trait AfterEach {
207
def after: Any
208
209
def apply[T: AsResult](a: => T): Result = {
210
try AsResult(a)
211
finally after
212
}
213
}
214
```
215
216
### AroundEach (mutable)
217
218
Wrap each example with custom logic.
219
220
```scala { .api }
221
trait AroundEach {
222
def around[T: AsResult](t: => T): Result
223
224
def apply[T: AsResult](a: => T): Result = around(a)
225
}
226
```
227
228
### BeforeAfterEach
229
230
Combined setup and cleanup.
231
232
```scala { .api }
233
trait BeforeAfterEach extends BeforeEach with AfterEach {
234
def apply[T: AsResult](a: => T): Result = {
235
before
236
try AsResult(a)
237
finally after
238
}
239
}
240
```
241
242
## Advanced Mutable Features
243
244
### Specification Scoping
245
246
Nested specification structure:
247
248
```scala
249
class DatabaseSpec extends Specification {
250
"Database operations" should {
251
"User operations" should {
252
"create users" in { /* test */ }
253
"update users" in { /* test */ }
254
"delete users" in { /* test */ }
255
}
256
257
"Order operations" should {
258
"create orders" in { /* test */ }
259
"process orders" in { /* test */ }
260
}
261
}
262
}
263
```
264
265
### Specification Arguments (mutable)
266
267
Configure mutable specification behavior:
268
269
```scala { .api }
270
trait ArgumentsShortcuts {
271
def sequential: Arguments
272
def isolated: Arguments
273
def stopOnFail: Arguments
274
def plan: Arguments
275
def skipAll: Arguments
276
}
277
```
278
279
### Tags and Sections (mutable)
280
281
Organizing and filtering examples:
282
283
```scala { .api }
284
trait TagDsl {
285
def tag(names: String*): Unit
286
def section(name: String): Unit
287
}
288
```
289
290
**Usage:**
291
```scala
292
class TaggedSpec extends Specification {
293
"Feature X" should {
294
"work in normal cases" in {
295
// test code
296
} tag("unit", "fast")
297
298
"work under load" in {
299
// test code
300
} tag("integration", "slow")
301
}
302
}
303
```
304
305
## Usage Patterns
306
307
### Basic Mutable Pattern
308
309
```scala
310
import org.specs2.mutable._
311
312
class UserServiceSpec extends Specification {
313
"UserService" should {
314
val service = new UserService
315
316
"create new users" in {
317
val user = service.create("john", "john@test.com")
318
user.name must beEqualTo("john")
319
user.email must beEqualTo("john@test.com")
320
}
321
322
"find users by email" in {
323
service.create("jane", "jane@test.com")
324
val found = service.findByEmail("jane@test.com")
325
found must beSome.which(_.name == "jane")
326
}
327
}
328
}
329
```
330
331
### Setup and Teardown
332
333
```scala
334
class DatabaseSpec extends Specification with BeforeAfterEach {
335
def before = {
336
Database.createTables()
337
Database.seedTestData()
338
}
339
340
def after = {
341
Database.cleanup()
342
}
343
344
"Database operations" should {
345
"insert records" in {
346
val count = Database.insert(testRecord)
347
count must beEqualTo(1)
348
}
349
350
"query records" in {
351
val results = Database.query("SELECT * FROM users")
352
results must not(beEmpty)
353
}
354
}
355
}
356
```
357
358
### Shared Examples
359
360
```scala
361
trait UserBehavior {
362
def validUser = {
363
"have a valid name" in {
364
user.name must not(beEmpty)
365
}
366
367
"have a valid email" in {
368
user.email must beMatching(emailRegex)
369
}
370
}
371
372
def user: User
373
def emailRegex: String
374
}
375
376
class UserSpec extends Specification with UserBehavior {
377
def user = User("john", "john@test.com")
378
def emailRegex = ".*@.*\\..*"
379
380
"User" should {
381
validUser
382
383
"calculate age correctly" in {
384
user.copy(birthYear = 1990).age must beGreaterThan(30)
385
}
386
}
387
}
388
```
389
390
### Conditional Examples
391
392
```scala
393
class ConditionalSpec extends Specification {
394
"Feature" should {
395
if (System.getProperty("integration.tests") == "true") {
396
"work with external service" in {
397
// integration test code
398
}
399
}
400
401
"work offline" in {
402
// unit test code
403
}
404
}
405
}
406
```
407
408
## Best Practices
409
410
1. **Use descriptive nesting**: Create logical hierarchies with nested "should" blocks
411
2. **Keep setup minimal**: Use context traits for complex setup/teardown scenarios
412
3. **Group related tests**: Use nested structure to group related functionality
413
4. **Avoid shared mutable state**: Each example should be independent
414
5. **Use tags wisely**: Tag examples for filtering slow or integration tests
415
6. **Handle exceptions properly**: Use appropriate matchers for expected exceptions
416
7. **Document with text**: Add descriptive text blocks to explain complex scenarios