0
# Mocking Framework
1
2
Complete mocking system for creating controlled test doubles with expectation-based verification.
3
4
## Capabilities
5
6
### Mock Class
7
8
Base class for creating service mocks with expectation management.
9
10
```scala { .api }
11
/**
12
* Base class for service mocks
13
* @tparam M - Service type (usually Has[Service])
14
*/
15
abstract class Mock[-M <: Has[_]] {
16
/** Creates expectation for specific capability */
17
def expects[I, A](capability: Capability[M, I, A]): Expectation[I]
18
19
/** Composes this mock with another mock */
20
def compose[M1 <: Has[_]](that: Mock[M1]): Mock[M with M1]
21
}
22
```
23
24
### Expectation System
25
26
Expectation types for defining mock behavior and verification.
27
28
```scala { .api }
29
/**
30
* Represents an expectation for mock method calls
31
* @tparam I - Input parameter type
32
*/
33
sealed trait Expectation[-I] {
34
/** Combines with another expectation (both must be satisfied) */
35
def and[I1 <: I](that: Expectation[I1]): Expectation[I1]
36
37
/** Alternative expectation (either can be satisfied) */
38
def or[I1 <: I](that: Expectation[I1]): Expectation[I1]
39
40
/** Repeats expectation for specified range of calls */
41
def repeats(range: Range): Expectation[I]
42
43
/** Specifies return value for matching calls */
44
def returns[A](value: A): Expectation[I]
45
46
/** Specifies effectful return value */
47
def returnsM[R, E, A](effect: ZIO[R, E, A]): Expectation[I]
48
49
/** Specifies that calls should throw error */
50
def throws[E](error: E): Expectation[I]
51
52
/** Specifies that calls should die with throwable */
53
def dies(throwable: Throwable): Expectation[I]
54
}
55
56
object Expectation {
57
/** Expectation that matches specific value */
58
def value[A](assertion: Assertion[A]): Expectation[A]
59
60
/** Expectation that matches any value */
61
val unit: Expectation[Unit]
62
63
/** Expectation that never matches */
64
val never: Expectation[Any]
65
}
66
```
67
68
### Capability System
69
70
Capability represents a mockable method or operation of a service.
71
72
```scala { .api }
73
/**
74
* Represents a capability (method) that can be mocked
75
* @tparam R - Service environment type
76
* @tparam I - Input parameter type
77
* @tparam A - Return type
78
*/
79
case class Capability[R <: Has[_], I, A](name: String) {
80
/** Creates expectation for this capability */
81
def apply(assertion: Assertion[I]): Expectation[I]
82
}
83
84
object Capability {
85
/** Creates capability with given name */
86
def of[M <: Has[_], I, A](name: String): Capability[M, I, A]
87
}
88
```
89
90
### Mock Result Types
91
92
Result types for specifying mock return values and behaviors.
93
94
```scala { .api }
95
/**
96
* Result of a mock method call
97
*/
98
sealed trait Result
99
100
object Result {
101
/** Successful result with value */
102
def succeed[A](value: A): Result
103
104
/** Failed result with error */
105
def fail[E](error: E): Result
106
107
/** Death result with throwable */
108
def die(throwable: Throwable): Result
109
110
/** Empty/unit result */
111
val unit: Result
112
}
113
```
114
115
### Proxy System
116
117
System for creating dynamic proxy instances from mocks.
118
119
```scala { .api }
120
/**
121
* Proxy factory for creating service instances from mocks
122
*/
123
object Proxy {
124
/** Creates service proxy from mock */
125
def make[R <: Has[_]](mock: Mock[R]): ULayer[R]
126
}
127
```
128
129
### Built-in Service Mocks
130
131
Pre-built mocks for standard ZIO services.
132
133
```scala { .api }
134
/**
135
* Mock for Clock service
136
*/
137
object MockClock extends Mock[Clock] {
138
/** currentTime capability */
139
val CurrentTime: Capability[Clock, Any, OffsetDateTime]
140
141
/** currentDateTime capability */
142
val CurrentDateTime: Capability[Clock, Any, OffsetDateTime]
143
144
/** nanoTime capability */
145
val NanoTime: Capability[Clock, Any, Long]
146
147
/** sleep capability */
148
val Sleep: Capability[Clock, Duration, Unit]
149
}
150
151
/**
152
* Mock for Console service
153
*/
154
object MockConsole extends Mock[Console] {
155
/** putStr capability */
156
val PutStr: Capability[Console, String, Unit]
157
158
/** putStrLn capability */
159
val PutStrLn: Capability[Console, String, Unit]
160
161
/** putStrErr capability */
162
val PutStrErr: Capability[Console, String, Unit]
163
164
/** getStrLn capability */
165
val GetStrLn: Capability[Console, Any, String]
166
}
167
168
/**
169
* Mock for Random service
170
*/
171
object MockRandom extends Mock[Random] {
172
/** nextBoolean capability */
173
val NextBoolean: Capability[Random, Any, Boolean]
174
175
/** nextBytes capability */
176
val NextBytes: Capability[Random, Int, Chunk[Byte]]
177
178
/** nextDouble capability */
179
val NextDouble: Capability[Random, Any, Double]
180
181
/** nextFloat capability */
182
val NextFloat: Capability[Random, Any, Float]
183
184
/** nextInt capability */
185
val NextInt: Capability[Random, Any, Int]
186
187
/** nextIntBounded capability */
188
val NextIntBounded: Capability[Random, Int, Int]
189
190
/** nextLong capability */
191
val NextLong: Capability[Random, Any, Long]
192
}
193
194
/**
195
* Mock for System service
196
*/
197
object MockSystem extends Mock[System] {
198
/** env capability */
199
val Env: Capability[System, String, Option[String]]
200
201
/** property capability */
202
val Property: Capability[System, String, Option[String]]
203
204
/** lineSeparator capability */
205
val LineSeparator: Capability[System, Any, String]
206
}
207
```
208
209
## Usage Examples
210
211
### Basic Mock Usage
212
213
```scala
214
import zio.test._
215
import zio.test.mock._
216
217
// Define service interface
218
trait UserService {
219
def getUser(id: String): Task[User]
220
def createUser(user: User): Task[String]
221
}
222
223
// Create mock object
224
object MockUserService extends Mock[Has[UserService]] {
225
object GetUser extends Effect[String, Throwable, User]
226
object CreateUser extends Effect[User, Throwable, String]
227
228
val compose: URLayer[Has[Proxy], Has[UserService]] =
229
ZLayer.fromService { proxy =>
230
new UserService {
231
def getUser(id: String) = proxy(GetUser, id)
232
def createUser(user: User) = proxy(CreateUser, user)
233
}
234
}
235
}
236
237
// Use in tests
238
test("user service mock") {
239
val mockLayer = MockUserService.GetUser(
240
Expectation.value(equalTo("123")).returns(User("123", "John"))
241
)
242
243
val program = for {
244
userService <- ZIO.service[UserService]
245
user <- userService.getUser("123")
246
} yield user
247
248
assertM(program)(hasField("name", _.name, equalTo("John")))
249
.provideLayer(mockLayer)
250
}
251
```
252
253
### Complex Expectations
254
255
```scala
256
test("complex mock expectations") {
257
val mockLayer =
258
MockUserService.GetUser(
259
Expectation.value(equalTo("user1")).returns(User("user1", "Alice"))
260
) &&
261
MockUserService.GetUser(
262
Expectation.value(equalTo("user2")).returns(User("user2", "Bob"))
263
) &&
264
MockUserService.CreateUser(
265
Expectation.value(hasField("name", _.name, startsWith("Test")))
266
.returns("new-id-123")
267
.repeats(1 to 3)
268
)
269
270
val program = for {
271
service <- ZIO.service[UserService]
272
user1 <- service.getUser("user1")
273
user2 <- service.getUser("user2")
274
id1 <- service.createUser(User("", "Test User 1"))
275
id2 <- service.createUser(User("", "Test User 2"))
276
} yield (user1, user2, id1, id2)
277
278
assertM(program) {
279
case (u1, u2, id1, id2) =>
280
assert(u1.name)(equalTo("Alice")) &&
281
assert(u2.name)(equalTo("Bob")) &&
282
assert(id1)(equalTo("new-id-123")) &&
283
assert(id2)(equalTo("new-id-123"))
284
}.provideLayer(mockLayer)
285
}
286
```
287
288
### Service Mocks with Built-ins
289
290
```scala
291
test("built-in service mocks") {
292
val clockMock = MockClock.CurrentTime(
293
Expectation.unit.returns(OffsetDateTime.now())
294
) ++ MockClock.Sleep(
295
Expectation.value(equalTo(1.second)).returns(())
296
)
297
298
val consoleMock = MockConsole.PutStrLn(
299
Expectation.value(equalTo("Hello")).returns(())
300
) ++ MockConsole.GetStrLn(
301
Expectation.unit.returns("input")
302
)
303
304
val program = for {
305
_ <- clock.currentTime
306
_ <- clock.sleep(1.second)
307
_ <- putStrLn("Hello")
308
input <- getStrLn
309
} yield input
310
311
assertM(program)(equalTo("input"))
312
.provideLayer(clockMock ++ consoleMock)
313
}
314
```
315
316
### Error and Failure Expectations
317
318
```scala
319
test("mock failures and errors") {
320
val mockLayer = MockUserService.GetUser(
321
Expectation.value(equalTo("missing")).throws(new RuntimeException("User not found"))
322
) ++ MockUserService.CreateUser(
323
Expectation.value(hasField("name", _.name, isEmpty))
324
.fails(ValidationError("Name cannot be empty"))
325
)
326
327
val program1 = ZIO.service[UserService].flatMap(_.getUser("missing"))
328
val program2 = ZIO.service[UserService].flatMap(_.createUser(User("", "")))
329
330
assertM(program1.run)(dies(hasMessage(containsString("User not found")))) &&
331
assertM(program2.run)(fails(isSubtype[ValidationError]))
332
.provideLayer(mockLayer)
333
}
334
```
335
336
### Verification and Call Counting
337
338
```scala
339
test("call verification") {
340
val mockLayer = MockUserService.GetUser(
341
Expectation.value(anything).returns(User("", "")).repeats(2 to 4)
342
)
343
344
val program = for {
345
service <- ZIO.service[UserService]
346
_ <- service.getUser("1")
347
_ <- service.getUser("2")
348
_ <- service.getUser("3")
349
} yield ()
350
351
assertM(program)(isUnit).provideLayer(mockLayer)
352
}
353
```