0
# Test Suite Styles
1
2
ScalaTest provides multiple testing styles to accommodate different testing preferences, team cultures, and domain-specific languages. Each style offers a different DSL for organizing and writing tests while sharing the same underlying execution engine and assertion capabilities.
3
4
## Capabilities
5
6
### FunSuite - Function-Based Testing
7
8
xUnit-style testing with named test functions. Ideal for unit testing and teams familiar with JUnit/TestNG.
9
10
```scala { .api }
11
/**
12
* Function-based test suite similar to xUnit frameworks
13
*/
14
abstract class AnyFunSuite extends Suite with TestSuite with Informing with Notifying with Alerting with Documenting {
15
/**
16
* Register a test with the given name and function
17
* @param testName the name of the test
18
* @param testFun the test function to execute
19
*/
20
protected def test(testName: String)(testFun: => Any): Unit
21
22
/**
23
* Register a test to be ignored
24
* @param testName the name of the ignored test
25
* @param testFun the test function (will not be executed)
26
*/
27
protected def ignore(testName: String)(testFun: => Any): Unit
28
}
29
30
/**
31
* Asynchronous version returning Future[Assertion]
32
*/
33
abstract class AsyncFunSuite extends AsyncTestSuite with TestSuite {
34
protected def test(testName: String)(testFun: => Future[compatible.Assertion]): Unit
35
protected def ignore(testName: String)(testFun: => Future[compatible.Assertion]): Unit
36
}
37
```
38
39
**Usage Example:**
40
41
```scala
42
import org.scalatest.funsuite.AnyFunSuite
43
import org.scalatest.matchers.should.Matchers
44
45
class MathUtilsSpec extends AnyFunSuite with Matchers {
46
47
test("addition should work correctly") {
48
val result = MathUtils.add(2, 3)
49
result should equal(5)
50
}
51
52
test("division by zero should throw exception") {
53
assertThrows[ArithmeticException] {
54
MathUtils.divide(10, 0)
55
}
56
}
57
58
ignore("complex calculation - not implemented yet") {
59
// Test will be ignored
60
pending
61
}
62
}
63
```
64
65
### FlatSpec - Flat Specification Style
66
67
Flat, linear test style that encourages descriptive test names. Good for behavior-driven development and readable test reports.
68
69
```scala { .api }
70
/**
71
* Flat specification style with subject-behavior syntax
72
*/
73
abstract class AnyFlatSpec extends Suite with TestSuite {
74
/**
75
* Define behavior for a subject using "should", "must", "can"
76
* Usage: "Subject" should "behavior description" in { test }
77
*/
78
protected def behavior(description: String): Unit
79
80
/**
81
* Define ignored behavior
82
* Usage: "Subject" should "behavior" ignore { test }
83
*/
84
protected def ignore: Unit
85
}
86
87
abstract class AsyncFlatSpec extends AsyncTestSuite with TestSuite {
88
protected def behavior(description: String): Unit
89
}
90
```
91
92
**Usage Example:**
93
94
```scala
95
import org.scalatest.flatspec.AnyFlatSpec
96
import org.scalatest.matchers.should.Matchers
97
98
class StringUtilsSpec extends AnyFlatSpec with Matchers {
99
100
behavior of "StringUtils"
101
102
it should "reverse strings correctly" in {
103
StringUtils.reverse("hello") should equal("olleh")
104
StringUtils.reverse("") should equal("")
105
}
106
107
it should "capitalize first letter" in {
108
StringUtils.capitalize("hello") should equal("Hello")
109
StringUtils.capitalize("HELLO") should equal("HELLO")
110
}
111
112
"The reverse method" should "handle null input gracefully" in {
113
StringUtils.reverse(null) should be(null)
114
}
115
116
it should "work with unicode characters" ignore {
117
// Test not implemented yet
118
pending
119
}
120
}
121
```
122
123
### WordSpec - Word-Based Specification
124
125
Specification style using nested contexts with "when", "should", "must", and "can". Excellent for complex behavior specification and acceptance testing.
126
127
```scala { .api }
128
/**
129
* Word-based specification style with nested contexts
130
*/
131
abstract class AnyWordSpec extends Suite with TestSuite {
132
/**
133
* Create nested specification contexts
134
* Usage: "Subject" when { "condition" should { "behavior" in { test } } }
135
*/
136
protected def when: WordSpecStringWrapper
137
protected def should: WordSpecStringWrapper
138
protected def must: WordSpecStringWrapper
139
protected def can: WordSpecStringWrapper
140
protected def which: WordSpecStringWrapper
141
}
142
143
abstract class AsyncWordSpec extends AsyncTestSuite with TestSuite
144
```
145
146
**Usage Example:**
147
148
```scala
149
import org.scalatest.wordspec.AnyWordSpec
150
import org.scalatest.matchers.should.Matchers
151
152
class BankAccountSpec extends AnyWordSpec with Matchers {
153
154
"A BankAccount" when {
155
"newly created" should {
156
"have zero balance" in {
157
val account = new BankAccount()
158
account.balance should equal(0.0)
159
}
160
161
"accept positive deposits" in {
162
val account = new BankAccount()
163
account.deposit(100.0)
164
account.balance should equal(100.0)
165
}
166
}
167
168
"with sufficient funds" should {
169
"allow withdrawals" in {
170
val account = new BankAccount()
171
account.deposit(100.0)
172
account.withdraw(50.0)
173
account.balance should equal(50.0)
174
}
175
}
176
177
"with insufficient funds" must {
178
"reject withdrawals" in {
179
val account = new BankAccount()
180
account.deposit(50.0)
181
assertThrows[InsufficientFundsException] {
182
account.withdraw(100.0)
183
}
184
}
185
}
186
}
187
}
188
```
189
190
### FreeSpec - Free-Form Specification
191
192
Free-form specification style with flexible nesting using dashes. Allows natural language test descriptions with arbitrary nesting levels.
193
194
```scala { .api }
195
/**
196
* Free-form specification style with dash-based nesting
197
*/
198
abstract class AnyFreeSpec extends Suite with TestSuite {
199
/**
200
* Create free-form nested specifications
201
* Usage: "description" - { test or nested specs }
202
*/
203
protected def -(text: String): FreeSpecStringWrapper
204
205
/**
206
* Ignore a specification
207
* Usage: "description" ignore { test }
208
*/
209
protected def ignore: Unit
210
}
211
212
abstract class AsyncFreeSpec extends AsyncTestSuite with TestSuite
213
```
214
215
**Usage Example:**
216
217
```scala
218
import org.scalatest.freespec.AnyFreeSpec
219
import org.scalatest.matchers.should.Matchers
220
221
class UserServiceSpec extends AnyFreeSpec with Matchers {
222
223
"UserService" - {
224
"when creating a new user" - {
225
"should validate email format" in {
226
val service = new UserService()
227
assertThrows[InvalidEmailException] {
228
service.createUser("john", "invalid-email")
229
}
230
}
231
232
"should generate unique ID" in {
233
val service = new UserService()
234
val user1 = service.createUser("john", "john@example.com")
235
val user2 = service.createUser("jane", "jane@example.com")
236
user1.id should not equal user2.id
237
}
238
239
"with duplicate email" - {
240
"should throw exception" in {
241
val service = new UserService()
242
service.createUser("john", "john@example.com")
243
assertThrows[DuplicateEmailException] {
244
service.createUser("jane", "john@example.com")
245
}
246
}
247
}
248
}
249
250
"when retrieving users" - {
251
"should return empty list initially" ignore {
252
// Test not implemented
253
pending
254
}
255
}
256
}
257
}
258
```
259
260
### FunSpec - Function Specification Style
261
262
RSpec-like specification style with "describe" and "it" blocks. Popular for behavior-driven development with natural language descriptions.
263
264
```scala { .api }
265
/**
266
* Function specification style similar to RSpec
267
*/
268
abstract class AnyFunSpec extends Suite with TestSuite {
269
/**
270
* Describe a component or behavior
271
* @param description what is being described
272
* @param fun nested specifications or tests
273
*/
274
protected def describe(description: String)(fun: => Unit): Unit
275
276
/**
277
* Specify individual behavior
278
* @param specText behavior description
279
* @param fun test implementation
280
*/
281
protected def it(specText: String)(fun: => Any): Unit
282
283
/**
284
* Ignore a specification
285
*/
286
protected def ignore(specText: String)(fun: => Any): Unit
287
}
288
289
abstract class AsyncFunSpec extends AsyncTestSuite with TestSuite {
290
protected def describe(description: String)(fun: => Unit): Unit
291
protected def it(specText: String)(fun: => Future[compatible.Assertion]): Unit
292
}
293
```
294
295
**Usage Example:**
296
297
```scala
298
import org.scalatest.funspec.AnyFunSpec
299
import org.scalatest.matchers.should.Matchers
300
301
class CalculatorSpec extends AnyFunSpec with Matchers {
302
303
describe("Calculator") {
304
describe("when performing addition") {
305
it("should add positive numbers correctly") {
306
val calc = new Calculator()
307
calc.add(2, 3) should equal(5)
308
}
309
310
it("should handle negative numbers") {
311
val calc = new Calculator()
312
calc.add(-2, 3) should equal(1)
313
calc.add(-2, -3) should equal(-5)
314
}
315
}
316
317
describe("when performing division") {
318
it("should divide numbers correctly") {
319
val calc = new Calculator()
320
calc.divide(10, 2) should equal(5.0)
321
}
322
323
it("should handle division by zero") {
324
val calc = new Calculator()
325
assertThrows[ArithmeticException] {
326
calc.divide(10, 0)
327
}
328
}
329
330
ignore("should handle floating point precision") {
331
// Complex floating point test - not implemented yet
332
pending
333
}
334
}
335
}
336
}
337
```
338
339
### FeatureSpec - Feature Specification Style
340
341
Gherkin-inspired BDD style with "Feature" and "Scenario" keywords. Ideal for acceptance testing and stakeholder communication.
342
343
```scala { .api }
344
/**
345
* Feature-driven specification style inspired by Gherkin/Cucumber
346
*/
347
abstract class AnyFeatureSpec extends Suite with TestSuite {
348
/**
349
* Define a feature
350
* @param description feature description
351
* @param fun scenarios for this feature
352
*/
353
protected def Feature(description: String)(fun: => Unit): Unit
354
355
/**
356
* Define a scenario within a feature
357
* @param description scenario description
358
* @param fun test implementation
359
*/
360
protected def Scenario(description: String)(fun: => Any): Unit
361
362
/**
363
* Ignore a scenario
364
*/
365
protected def ignore(description: String)(fun: => Any): Unit
366
}
367
368
abstract class AsyncFeatureSpec extends AsyncTestSuite with TestSuite {
369
protected def Feature(description: String)(fun: => Unit): Unit
370
protected def Scenario(description: String)(fun: => Future[compatible.Assertion]): Unit
371
}
372
```
373
374
**Usage Example:**
375
376
```scala
377
import org.scalatest.featurespec.AnyFeatureSpec
378
import org.scalatest.matchers.should.Matchers
379
import org.scalatest.GivenWhenThen
380
381
class ShoppingCartSpec extends AnyFeatureSpec with Matchers with GivenWhenThen {
382
383
Feature("Shopping Cart Management") {
384
385
Scenario("Adding items to empty cart") {
386
Given("an empty shopping cart")
387
val cart = new ShoppingCart()
388
cart.items should be empty
389
390
When("I add an item")
391
val item = Item("Book", 29.99)
392
cart.addItem(item)
393
394
Then("the cart should contain the item")
395
cart.items should contain(item)
396
cart.total should equal(29.99)
397
}
398
399
Scenario("Removing items from cart") {
400
Given("a cart with items")
401
val cart = new ShoppingCart()
402
val book = Item("Book", 29.99)
403
val pen = Item("Pen", 2.50)
404
cart.addItem(book)
405
cart.addItem(pen)
406
407
When("I remove an item")
408
cart.removeItem(book)
409
410
Then("the cart should not contain that item")
411
cart.items should not contain book
412
cart.items should contain(pen)
413
cart.total should equal(2.50)
414
}
415
416
ignore("Applying discount codes") {
417
// Feature not yet implemented
418
pending
419
}
420
}
421
}
422
```
423
424
### PropSpec - Property-Based Testing Style
425
426
Property-based testing style for defining properties that should hold for ranges of data. Integrates well with property testing libraries.
427
428
```scala { .api }
429
/**
430
* Property-based testing specification style
431
*/
432
abstract class AnyPropSpec extends Suite with TestSuite {
433
/**
434
* Define a property that should hold
435
* @param testName property description
436
* @param fun property test implementation
437
*/
438
protected def property(testName: String)(fun: => Any): Unit
439
440
/**
441
* Ignore a property
442
*/
443
protected def ignore(testName: String)(fun: => Any): Unit
444
}
445
446
abstract class AsyncPropSpec extends AsyncTestSuite with TestSuite {
447
protected def property(testName: String)(fun: => Future[compatible.Assertion]): Unit
448
}
449
```
450
451
**Usage Example:**
452
453
```scala
454
import org.scalatest.propspec.AnyPropSpec
455
import org.scalatest.matchers.should.Matchers
456
457
class StringPropertiesSpec extends AnyPropSpec with Matchers {
458
459
property("string reverse is idempotent") {
460
forAll { (s: String) =>
461
StringUtils.reverse(StringUtils.reverse(s)) should equal(s)
462
}
463
}
464
465
property("string length is preserved by reverse") {
466
forAll { (s: String) =>
467
StringUtils.reverse(s).length should equal(s.length)
468
}
469
}
470
471
property("concatenation length is sum of individual lengths") {
472
forAll { (s1: String, s2: String) =>
473
(s1 + s2).length should equal(s1.length + s2.length)
474
}
475
}
476
477
ignore("complex unicode normalization properties") {
478
// Complex unicode property tests - not implemented
479
pending
480
}
481
}
482
```
483
484
## Fixture Support
485
486
All test styles support fixture variants for managing test setup and teardown:
487
488
### Fixture Variants
489
490
```scala { .api }
491
// Fixture variants available for all styles
492
abstract class FixtureAnyFunSuite extends fixture.TestSuite
493
abstract class FixtureAnyFlatSpec extends fixture.TestSuite
494
abstract class FixtureAnyWordSpec extends fixture.TestSuite
495
abstract class FixtureAnyFreeSpec extends fixture.TestSuite
496
abstract class FixtureAnyFunSpec extends fixture.TestSuite
497
abstract class FixtureAnyFeatureSpec extends fixture.TestSuite
498
abstract class FixtureAnyPropSpec extends fixture.TestSuite
499
500
// Async fixture variants
501
abstract class FixtureAsyncFunSuite extends fixture.AsyncTestSuite
502
abstract class FixtureAsyncFlatSpec extends fixture.AsyncTestSuite
503
// ... similar pattern for all async variants
504
```
505
506
### Choosing a Test Style
507
508
**FunSuite**: Best for unit testing, simple test cases, teams familiar with xUnit frameworks
509
**FlatSpec**: Good for behavior specification, flat test structure, readable reports
510
**WordSpec**: Ideal for complex behavior specification, nested contexts, acceptance testing
511
**FreeSpec**: Perfect for natural language descriptions, flexible organization, documentation-like tests
512
**FunSpec**: Great for BDD, hierarchical organization, teams familiar with RSpec
513
**FeatureSpec**: Excellent for acceptance testing, stakeholder communication, Gherkin-style scenarios
514
**PropSpec**: Essential for property-based testing, mathematical properties, comprehensive test coverage
515
516
All styles can be mixed within the same test suite using `Suites` wrapper and support the same assertion and matcher capabilities.