0
# Assertions
1
2
ScalaTest's assertion framework provides the foundation for all test verification with enhanced error reporting, stack trace management, and integration with the matcher system. The framework distinguishes between assertions (which fail tests) and assumptions (which cancel tests when conditions aren't met).
3
4
## Capabilities
5
6
### Core Assertions
7
8
The fundamental assertion methods available in all test suites through the `Assertions` trait.
9
10
```scala { .api }
11
/**
12
* Core assertion methods for test verification
13
*/
14
trait Assertions {
15
/**
16
* Assert that a boolean condition is true
17
* @param condition the boolean condition to check
18
* @throws TestFailedException if condition is false
19
*/
20
def assert(condition: Boolean): Assertion
21
22
/**
23
* Assert that a boolean condition is true with additional clue
24
* @param condition the boolean condition to check
25
* @param clue additional information displayed on failure
26
*/
27
def assert(condition: Boolean, clue: Any): Assertion
28
29
/**
30
* Assert that actual value equals expected value
31
* @param expected the expected value
32
* @param actual the actual value to compare
33
*/
34
def assertResult[T](expected: T)(actual: T): Assertion
35
36
/**
37
* Assert that executing code throws expected exception type
38
* @param f the code block that should throw
39
* @return the thrown exception
40
*/
41
def assertThrows[T <: AnyRef](f: => Any): T
42
43
/**
44
* Fail the test immediately with default message
45
*/
46
def fail(): Nothing
47
48
/**
49
* Fail the test immediately with custom message
50
* @param message the failure message
51
*/
52
def fail(message: String): Nothing
53
54
/**
55
* Cancel the test (different from failure - indicates test couldn't run)
56
*/
57
def cancel(): Nothing
58
59
/**
60
* Cancel the test with message
61
* @param message the cancellation reason
62
*/
63
def cancel(message: String): Nothing
64
65
/**
66
* Mark test as pending (not yet implemented)
67
*/
68
def pending: Assertion with PendingStatement
69
70
/**
71
* Explicit success assertion (useful in some contexts)
72
*/
73
def succeed: Assertion
74
}
75
```
76
77
**Usage Examples:**
78
79
```scala
80
import org.scalatest.funsuite.AnyFunSuite
81
import org.scalatest.Assertions
82
83
class AssertionExamplesSpec extends AnyFunSuite {
84
85
test("basic assertions") {
86
val x = 5
87
val y = 3
88
89
// Simple boolean assertion
90
assert(x > y)
91
assert(x > 0, "x should be positive")
92
93
// Value comparison
94
assertResult(8) {
95
x + y
96
}
97
98
// Exception assertion
99
assertThrows[ArithmeticException] {
100
x / 0
101
}
102
}
103
104
test("test lifecycle control") {
105
val config = loadTestConfiguration()
106
107
if (config.isEmpty) {
108
cancel("Configuration not available - test cannot run")
109
}
110
111
// Test continues only if config is available
112
processWithConfig(config.get)
113
succeed // Explicit success
114
}
115
116
test("pending implementation") {
117
// Test placeholder for future implementation
118
pending
119
}
120
}
121
```
122
123
### Enhanced Assertions with Clues
124
125
Add contextual information to assertion failures using `AppendedClues` trait.
126
127
```scala { .api }
128
/**
129
* Provides withClue method for adding context to assertions
130
*/
131
trait AppendedClues {
132
/**
133
* Execute assertion with additional clue information
134
* @param clue contextual information for failure messages
135
* @param fun assertion code block
136
*/
137
def withClue[T](clue: Any)(fun: => T): T
138
}
139
```
140
141
**Usage Example:**
142
143
```scala
144
import org.scalatest.funsuite.AnyFunSuite
145
import org.scalatest.matchers.should.Matchers
146
import org.scalatest.AppendedClues
147
148
class ClueExamplesSpec extends AnyFunSuite with Matchers with AppendedClues {
149
150
test("assertions with contextual clues") {
151
val users = loadUsers()
152
val activeUsers = users.filter(_.isActive)
153
154
withClue(s"Total users: ${users.size}, Active: ${activeUsers.size}") {
155
activeUsers.size should be > 0
156
}
157
158
for ((user, index) <- users.zipWithIndex) {
159
withClue(s"User at index $index: ${user.name}") {
160
user.email should not be empty
161
user.age should be >= 0
162
}
163
}
164
}
165
}
166
```
167
168
### Non-Implicit Assertions
169
170
Use `NonImplicitAssertions` when you want to avoid implicit conversions that enable the matcher DSL.
171
172
```scala { .api }
173
/**
174
* Assertion methods without implicit conversions to matchers
175
*/
176
trait NonImplicitAssertions {
177
def assert(condition: Boolean): Assertion
178
def assert(condition: Boolean, clue: Any): Assertion
179
def assertResult[T](expected: T)(actual: T): Assertion
180
def assertThrows[T <: AnyRef](f: => Any): T
181
// ... other assertion methods without matcher support
182
}
183
```
184
185
### Assumptions
186
187
Assumptions cancel tests when preconditions aren't met, different from assertions which fail tests.
188
189
```scala { .api }
190
/**
191
* Assumption methods for conditional test execution
192
*/
193
def assume(condition: Boolean): Assertion
194
def assume(condition: Boolean, clue: Any): Assertion
195
def cancel(): Nothing
196
def cancel(message: String): Nothing
197
```
198
199
**Usage Example:**
200
201
```scala
202
import org.scalatest.funsuite.AnyFunSuite
203
204
class DatabaseIntegrationSpec extends AnyFunSuite {
205
206
test("database operations require connection") {
207
val dbAvailable = checkDatabaseConnection()
208
209
// Cancel test if database isn't available (not a test failure)
210
assume(dbAvailable, "Database connection not available")
211
212
// Test continues only if database is available
213
val result = performDatabaseOperation()
214
assert(result.isSuccess)
215
}
216
}
217
```
218
219
### Enhanced Value Access
220
221
Safely access contents of common Scala types with helpful error messages.
222
223
```scala { .api }
224
/**
225
* Safe access to Option contents
226
*/
227
trait OptionValues {
228
implicit def convertOptionToValuable[T](opt: Option[T]): OptionValuable[T]
229
230
final class OptionValuable[T](opt: Option[T]) {
231
/**
232
* Get the value from Some, or fail with descriptive message for None
233
*/
234
def value: T
235
}
236
}
237
238
/**
239
* Safe access to Either contents
240
*/
241
trait EitherValues {
242
implicit def convertEitherToValuable[L, R](either: Either[L, R]): EitherValuable[L, R]
243
244
final class EitherValuable[L, R](either: Either[L, R]) {
245
def left: LeftValuable[L, R]
246
def right: RightValuable[L, R]
247
}
248
249
final class LeftValuable[L, R](either: Either[L, R]) {
250
def value: L // Fails if Either is Right
251
}
252
253
final class RightValuable[L, R](either: Either[L, R]) {
254
def value: R // Fails if Either is Left
255
}
256
}
257
258
/**
259
* Safe access to Try contents
260
*/
261
trait TryValues {
262
implicit def convertTryToSuccessOrFailure[T](t: Try[T]): TryValuable[T]
263
264
final class TryValuable[T](t: Try[T]) {
265
def success: SuccessValuable[T]
266
def failure: FailureValuable
267
}
268
269
final class SuccessValuable[T](t: Try[T]) {
270
def value: T // Fails if Try is Failure
271
}
272
273
final class FailureValuable(t: Try[_]) {
274
def exception: Throwable // Fails if Try is Success
275
}
276
}
277
278
/**
279
* Safe access to partial function behavior
280
*/
281
trait PartialFunctionValues {
282
implicit def convertPartialFunctionToValuable[A, B](pf: PartialFunction[A, B]): PartialFunctionValuable[A, B]
283
284
final class PartialFunctionValuable[A, B](pf: PartialFunction[A, B]) {
285
/**
286
* Apply partial function or fail with descriptive message
287
*/
288
def valueAt(x: A): B
289
}
290
}
291
292
/**
293
* Assert collections contain exactly one element
294
*/
295
trait LoneElement {
296
implicit def convertToCollectionLoneElementWrapper[E, C[_]](collection: C[E])(implicit conv: C[E] => Iterable[E]): CollectionLoneElementWrapper[C]
297
298
final class CollectionLoneElementWrapper[C[_]](collection: C[_]) {
299
/**
300
* Get the single element, or fail if empty or multiple elements
301
*/
302
def loneElement: Any
303
}
304
}
305
```
306
307
**Usage Examples:**
308
309
```scala
310
import org.scalatest.funsuite.AnyFunSuite
311
import org.scalatest.matchers.should.Matchers
312
import org.scalatest.{OptionValues, EitherValues, TryValues, PartialFunctionValues, LoneElement}
313
import scala.util.{Try, Success, Failure}
314
315
class ValueAccessSpec extends AnyFunSuite with Matchers
316
with OptionValues with EitherValues with TryValues
317
with PartialFunctionValues with LoneElement {
318
319
test("safe Option access") {
320
val someValue = Some("hello")
321
val noneValue = None
322
323
someValue.value should equal("hello")
324
325
// This would fail with descriptive message:
326
// noneValue.value should equal("anything")
327
}
328
329
test("safe Either access") {
330
val rightValue: Either[String, Int] = Right(42)
331
val leftValue: Either[String, Int] = Left("error")
332
333
rightValue.right.value should equal(42)
334
leftValue.left.value should equal("error")
335
336
// These would fail with descriptive messages:
337
// rightValue.left.value
338
// leftValue.right.value
339
}
340
341
test("safe Try access") {
342
val success: Try[Int] = Success(42)
343
val failure: Try[Int] = Failure(new RuntimeException("oops"))
344
345
success.success.value should equal(42)
346
failure.failure.exception.getMessage should equal("oops")
347
}
348
349
test("partial function testing") {
350
val pf: PartialFunction[Int, String] = {
351
case x if x > 0 => s"positive: $x"
352
}
353
354
pf.valueAt(5) should equal("positive: 5")
355
356
// This would fail with descriptive message:
357
// pf.valueAt(-1) should equal("anything")
358
}
359
360
test("lone element access") {
361
val singleItem = List("only")
362
val multipleItems = List("first", "second")
363
val emptyList = List.empty[String]
364
365
singleItem.loneElement should equal("only")
366
367
// These would fail with descriptive messages:
368
// multipleItems.loneElement
369
// emptyList.loneElement
370
}
371
}
372
```
373
374
### Exception Handling and Stack Traces
375
376
Control how exceptions and stack traces are handled in test failures.
377
378
```scala { .api }
379
/**
380
* Control stack trace display in test failures
381
*/
382
trait SeveredStackTraces {
383
// Automatically shortens stack traces to focus on relevant test code
384
}
385
386
/**
387
* Handle exceptions during test reporting
388
*/
389
trait CatchReporter {
390
// Catches and reports exceptions that occur during test reporting
391
}
392
```
393
394
### Custom Assertion Helpers
395
396
Create reusable assertion helpers for domain-specific testing.
397
398
**Usage Example:**
399
400
```scala
401
import org.scalatest.funsuite.AnyFunSuite
402
import org.scalatest.matchers.should.Matchers
403
import org.scalatest.Assertion
404
405
class CustomAssertionSpec extends AnyFunSuite with Matchers {
406
407
// Custom assertion helper
408
def assertValidEmail(email: String): Assertion = {
409
withClue(s"Invalid email format: '$email'") {
410
assert(email.contains("@"), "Email must contain @")
411
assert(email.contains("."), "Email must contain domain")
412
assert(!email.startsWith("@"), "Email cannot start with @")
413
assert(!email.endsWith("@"), "Email cannot end with @")
414
}
415
}
416
417
// Another custom helper
418
def assertBetween[T](value: T, min: T, max: T)(implicit ord: Ordering[T]): Assertion = {
419
withClue(s"Value $value not between $min and $max") {
420
assert(ord.gteq(value, min) && ord.lteq(value, max))
421
}
422
}
423
424
test("custom assertions in action") {
425
assertValidEmail("user@example.com")
426
assertBetween(5, 1, 10)
427
assertBetween(2.5, 2.0, 3.0)
428
429
// These would fail:
430
// assertValidEmail("invalid-email")
431
// assertBetween(15, 1, 10)
432
}
433
}
434
```
435
436
## Common Patterns
437
438
### Testing Exceptions
439
440
```scala
441
// Test that specific exception is thrown
442
assertThrows[IllegalArgumentException] {
443
new BankAccount(-100) // negative balance
444
}
445
446
// Capture and inspect the exception
447
val exception = assertThrows[ValidationException] {
448
validator.validate(invalidData)
449
}
450
exception.getMessage should include("required field")
451
exception.getFieldName should equal("email")
452
```
453
454
### Conditional Testing
455
456
```scala
457
// Use assumptions for environment-dependent tests
458
assume(System.getProperty("env") == "integration", "Integration tests only")
459
460
// Use cancellation for unavailable resources
461
if (!externalServiceAvailable) {
462
cancel("External service unavailable")
463
}
464
```
465
466
### Batch Assertions
467
468
```scala
469
// Multiple related assertions with context
470
withClue("User validation failed") {
471
user.name should not be empty
472
user.email should include("@")
473
user.age should be >= 18
474
}
475
```