0
# Smart Assertions (Scala 3)
1
2
Compile-time smart assertions with automatic expression analysis and enhanced error reporting. Smart assertions provide more intuitive test writing with better error messages by analyzing expressions at compile time.
3
4
## Capabilities
5
6
### Smart Assert
7
8
The primary smart assertion function that analyzes boolean expressions at compile time.
9
10
```scala { .api }
11
/**
12
* Smart assertion with compile-time expression analysis
13
* @param assertion - Boolean expression to test (analyzed at compile time)
14
* @return TestResult with detailed error reporting
15
*/
16
inline def assertTrue(inline assertion: Boolean)(implicit
17
trace: Trace,
18
sourceLocation: SourceLocation
19
): TestResult
20
```
21
22
**Usage Examples:**
23
24
```scala
25
import zio.test._
26
27
test("smart assertions") {
28
val user = User("Alice", 25, active = true)
29
val numbers = List(1, 2, 3, 4, 5)
30
31
// Simple comparisons with enhanced error messages
32
assertTrue(user.age >= 18)
33
assertTrue(user.name.nonEmpty)
34
assertTrue(user.active)
35
36
// Complex expressions are analyzed
37
assertTrue(numbers.size == 5 && numbers.contains(3))
38
assertTrue(user.age > 20 && user.name.startsWith("A"))
39
40
// Method calls are captured
41
assertTrue(numbers.head == 1)
42
assertTrue(numbers.last == 5)
43
assertTrue(user.name.length > 2)
44
}
45
```
46
47
### Smart Assert with Explicit Assertion
48
49
Smart assertion that applies a traditional assertion to a value with compile-time expression capture.
50
51
```scala { .api }
52
/**
53
* Smart assertion for single values with traditional assertions
54
* @param value - Value to test (expression captured at compile time)
55
* @param assertion - Assertion to apply to the value
56
* @return TestResult with enhanced error reporting
57
*/
58
inline def assert[A](inline value: A)(inline assertion: Assertion[A])(implicit
59
trace: Trace,
60
sourceLocation: SourceLocation
61
): TestResult
62
```
63
64
**Usage Examples:**
65
66
```scala
67
import zio.test._
68
import zio.test.Assertion._
69
70
test("smart assert with explicit assertions") {
71
val numbers = List(1, 2, 3, 4, 5)
72
val name = "Alice"
73
val age = 25
74
75
// Explicit assertions with smart error reporting
76
assert(numbers)(hasSize(equalTo(5)))
77
assert(numbers)(contains(3))
78
assert(numbers)(forall(isGreaterThan(0)))
79
80
assert(name)(hasLength(equalTo(5)))
81
assert(name)(startsWith("A"))
82
83
assert(age)(isGreaterThanEqualTo(18))
84
assert(age)(isLessThan(100))
85
}
86
```
87
88
### Smart Assert ZIO
89
90
Smart assertion for effectful computations with compile-time expression analysis.
91
92
```scala { .api }
93
/**
94
* Smart assertion for effectful values
95
* @param effect - ZIO effect producing value to test
96
* @param assertion - Assertion to apply to the effect result
97
* @return ZIO effect producing TestResult
98
*/
99
inline def assertZIO[R, E, A](effect: ZIO[R, E, A])(inline assertion: Assertion[A])(implicit
100
trace: Trace,
101
sourceLocation: SourceLocation
102
): ZIO[R, E, TestResult]
103
```
104
105
**Usage Examples:**
106
107
```scala
108
import zio.test._
109
import zio.test.Assertion._
110
111
test("smart assertZIO") {
112
// Testing effectful computations
113
assertZIO(ZIO.succeed(42))(equalTo(42)) *>
114
assertZIO(ZIO.succeed("hello"))(hasLength(equalTo(5))) *>
115
assertZIO(ZIO.succeed(List(1, 2, 3)))(hasSize(equalTo(3)))
116
}
117
118
test("assertZIO with service calls") {
119
for {
120
// Testing service method results
121
result1 <- assertZIO(userService.findById(1))(isSome(anything))
122
result2 <- assertZIO(userService.count())(isGreaterThan(0))
123
result3 <- assertZIO(mathService.add(2, 3))(equalTo(5))
124
} yield result1 && result2 && result3
125
}
126
```
127
128
### Type Checking (Compile-time)
129
130
Compile-time type checking for ensuring code compiles or fails to compile as expected.
131
132
```scala { .api }
133
/**
134
* Compile-time type checking (Scala 3)
135
* @param code - Code to check for compilation
136
* @return UIO[Either[String, Unit]] - Left with error messages if compilation fails, Right with Unit if successful
137
*/
138
inline def typeCheck(inline code: String): UIO[Either[String, Unit]]
139
140
/**
141
* Compile-time type checking (Scala 2)
142
* @param code - Code to check for compilation
143
* @return UIO[Either[String, Unit]] - Left with error messages if compilation fails, Right with Unit if successful
144
*/
145
def typeCheck(code: String): UIO[Either[String, Unit]]
146
```
147
148
**Usage Examples:**
149
150
```scala
151
import zio.test._
152
153
test("type checking") {
154
for {
155
// Ensure valid code compiles
156
validResult <- typeCheck("val x: Int = 42")
157
_ <- assertTrue(validResult.isRight)
158
159
// Ensure invalid code fails to compile
160
invalidResult <- typeCheck("val x: String = 42")
161
_ <- assertTrue(invalidResult.isLeft)
162
163
// Test generic type inference
164
genericResult <- typeCheck("List(1, 2, 3).map(_ * 2)")
165
_ <- assertTrue(genericResult.isRight)
166
} yield ()
167
}
168
```
169
170
### Smart Assertion Extensions
171
172
Extension methods for navigating complex data structures in smart assertions.
173
174
```scala { .api }
175
/**
176
* Extension method for navigating data in smart assertions
177
*/
178
implicit final class SmartAssertionOps[A](private val self: A) extends AnyVal {
179
/**
180
* Transform the value using the given TestLens for navigation
181
* @param f - Function to transform TestLens
182
* @return Transformed value for assertion
183
*/
184
def is[B](f: TestLens[A] => TestLens[B]): B
185
}
186
187
/**
188
* TestLens for navigating Option values
189
*/
190
implicit final class TestLensOptionOps[A](private val self: TestLens[Option[A]]) extends AnyVal {
191
/** Transform Option to its Some value, fails if None */
192
def some: TestLens[A]
193
}
194
195
/**
196
* TestLens for navigating Either values
197
*/
198
implicit final class TestLensEitherOps[E, A](private val self: TestLens[Either[E, A]]) extends AnyVal {
199
/** Transform Either to its Left value, fails if Right */
200
def left: TestLens[E]
201
202
/** Transform Either to its Right value, fails if Left */
203
def right: TestLens[A]
204
}
205
206
/**
207
* TestLens for navigating Try values
208
*/
209
implicit final class TestLensTryOps[A](private val self: TestLens[scala.util.Try[A]]) extends AnyVal {
210
/** Transform Try to its Success value, fails if Failure */
211
def success: TestLens[A]
212
213
/** Transform Try to Throwable if Failure, fails if Success */
214
def failure: TestLens[Throwable]
215
}
216
217
/**
218
* TestLens for navigating Exit values
219
*/
220
implicit final class TestLensExitOps[E, A](private val self: TestLens[Exit[E, A]]) extends AnyVal {
221
/** Transform Exit to Throwable if die, fails otherwise */
222
def die: TestLens[Throwable]
223
224
/** Transform Exit to failure type if fail, fails otherwise */
225
def failure: TestLens[E]
226
227
/** Transform Exit to success type if succeed, fails otherwise */
228
def success: TestLens[A]
229
230
/** Transform Exit to its underlying Cause if it has one */
231
def cause: TestLens[Cause[E]]
232
233
/** Transform Exit to boolean representing if interrupted */
234
def interrupted: TestLens[Boolean]
235
}
236
```
237
238
**Usage Examples:**
239
240
```scala
241
import zio.test._
242
243
test("smart assertion navigation") {
244
val maybeUser: Option[User] = Some(User("Alice", 25))
245
val result: Either[String, Int] = Right(42)
246
val computation: Try[String] = Success("hello")
247
248
// Navigate Option values
249
assertTrue(maybeUser.is(_.some.name) == "Alice")
250
assertTrue(maybeUser.is(_.some.age) > 18)
251
252
// Navigate Either values
253
assertTrue(result.is(_.right) == 42)
254
assertTrue(result.is(_.right) > 40)
255
256
// Navigate Try values
257
assertTrue(computation.is(_.success.length) == 5)
258
assertTrue(computation.is(_.success.startsWith("h")))
259
}
260
261
test("exit navigation") {
262
val success: Exit[String, Int] = Exit.succeed(42)
263
val failure: Exit[String, Int] = Exit.fail("error")
264
265
assertTrue(success.is(_.success) == 42)
266
assertTrue(failure.is(_.failure) == "error")
267
}
268
```
269
270
### Custom Assertions
271
272
Interface for creating custom smart assertion transformations.
273
274
```scala { .api }
275
/**
276
* Interface for custom assertion transformations
277
*/
278
trait CustomAssertion[A, B] {
279
def apply(value: A): Either[String, B]
280
}
281
282
/**
283
* TestLens extension for custom assertions
284
*/
285
implicit final class TestLensAnyOps[A](private val self: TestLens[A]) extends AnyVal {
286
/** Transform value with custom assertion */
287
def custom[B](customAssertion: CustomAssertion[A, B]): TestLens[B]
288
289
/** Transform value to subtype if possible */
290
def subtype[Subtype <: A]: TestLens[Subtype]
291
292
/** Always returns true if preceding transformations succeeded */
293
def anything: TestLens[Boolean]
294
}
295
```
296
297
**Usage Examples:**
298
299
```scala
300
import zio.test._
301
302
// Custom assertion for email validation
303
val emailAssertion = new CustomAssertion[String, String] {
304
def apply(value: String): Either[String, String] =
305
if (value.contains("@")) Right(value)
306
else Left("Not a valid email")
307
}
308
309
test("custom assertions") {
310
val email = "user@example.com"
311
val invalidEmail = "notanemail"
312
313
assertTrue(email.is(_.custom(emailAssertion).nonEmpty))
314
// invalidEmail.is(_.custom(emailAssertion)) would fail the test
315
316
// Subtype checking
317
val shapes: List[Shape] = List(Circle(5), Rectangle(4, 6))
318
assertTrue(shapes.head.is(_.subtype[Circle].radius) == 5)
319
}
320
```