0
# Mock Testing Framework
1
2
Service mocking system with expectation-based testing for ZIO services. The mock framework allows testing components in isolation by providing controlled implementations of their dependencies.
3
4
## Capabilities
5
6
### Core Mock Classes
7
8
Foundation classes for creating and using mocks.
9
10
```scala { .api }
11
/**
12
* Base trait for creating mock implementations of ZIO services
13
*/
14
trait Mock[R] {
15
/**
16
* The environment type this mock provides
17
*/
18
type Environment = R
19
}
20
21
/**
22
* Represents an expectation for a service method call
23
*/
24
abstract class Expectation[R] {
25
/**
26
* Combine this expectation with another using logical AND
27
*/
28
def &&[R1 <: R](that: Expectation[R1]): Expectation[R1]
29
30
/**
31
* Combine this expectation with another using logical OR
32
*/
33
def ||[R1 <: R](that: Expectation[R1]): Expectation[R1]
34
35
/**
36
* Repeat this expectation N times
37
*/
38
def repeats(range: Range): Expectation[R]
39
40
/**
41
* Make this expectation optional (may not be called)
42
*/
43
def optional: Expectation[R]
44
}
45
46
/**
47
* Represents a capability/method that can be mocked
48
*/
49
sealed trait Capability[R, I, E, A] {
50
/**
51
* The service type this capability belongs to
52
*/
53
type Service = R
54
55
/**
56
* Input parameters type
57
*/
58
type Input = I
59
60
/**
61
* Error type
62
*/
63
type Error = E
64
65
/**
66
* Return type
67
*/
68
type Output = A
69
}
70
71
/**
72
* Result that a mock method can return
73
*/
74
sealed trait Result[+E, +A]
75
76
object Result {
77
/**
78
* Successful result with value
79
*/
80
def succeed[A](value: A): Result[Nothing, A]
81
82
/**
83
* Failed result with error
84
*/
85
def fail[E](error: E): Result[E, Nothing]
86
87
/**
88
* Result that dies with exception
89
*/
90
def die(throwable: Throwable): Result[Nothing, Nothing]
91
92
/**
93
* Unit success result
94
*/
95
val unit: Result[Nothing, Unit]
96
}
97
```
98
99
### Built-in Mock Services
100
101
Pre-built mocks for standard ZIO services.
102
103
```scala { .api }
104
/**
105
* Mock implementation of ZIO Clock service
106
*/
107
object MockClock extends Mock[Clock] {
108
// Clock capabilities
109
object CurrentTime extends Capability[Clock, TimeUnit, Nothing, Long]
110
object CurrentDateTime extends Capability[Clock, Any, Nothing, OffsetDateTime]
111
object NanoTime extends Capability[Clock, Any, Nothing, Long]
112
object Sleep extends Capability[Clock, Duration, Nothing, Unit]
113
114
// Expectation builders
115
def currentTime(timeUnit: TimeUnit): Expectation[Clock]
116
def currentDateTime: Expectation[Clock]
117
def nanoTime: Expectation[Clock]
118
def sleep(duration: Duration): Expectation[Clock]
119
}
120
121
/**
122
* Mock implementation of ZIO Console service
123
*/
124
object MockConsole extends Mock[Console] {
125
// Console capabilities
126
object PutStr extends Capability[Console, String, IOException, Unit]
127
object PutStrLn extends Capability[Console, String, IOException, Unit]
128
object PutStrErr extends Capability[Console, String, IOException, Unit]
129
object PutStrLnErr extends Capability[Console, String, IOException, Unit]
130
object GetStrLn extends Capability[Console, Any, IOException, String]
131
132
// Expectation builders
133
def putStr(line: String): Expectation[Console]
134
def putStrLn(line: String): Expectation[Console]
135
def putStrErr(line: String): Expectation[Console]
136
def putStrLnErr(line: String): Expectation[Console]
137
def getStrLn: Expectation[Console]
138
}
139
140
/**
141
* Mock implementation of ZIO Random service
142
*/
143
object MockRandom extends Mock[Random] {
144
// Random capabilities
145
object NextBoolean extends Capability[Random, Any, Nothing, Boolean]
146
object NextBytes extends Capability[Random, Int, Nothing, Chunk[Byte]]
147
object NextDouble extends Capability[Random, Any, Nothing, Double]
148
object NextDoubleBetween extends Capability[Random, (Double, Double), Nothing, Double]
149
object NextFloat extends Capability[Random, Any, Nothing, Float]
150
object NextFloatBetween extends Capability[Random, (Float, Float), Nothing, Float]
151
object NextGaussian extends Capability[Random, Any, Nothing, Double]
152
object NextInt extends Capability[Random, Any, Nothing, Int]
153
object NextIntBetween extends Capability[Random, (Int, Int), Nothing, Int]
154
object NextIntBounded extends Capability[Random, Int, Nothing, Int]
155
object NextLong extends Capability[Random, Any, Nothing, Long]
156
object NextLongBetween extends Capability[Random, (Long, Long), Nothing, Long]
157
object NextLongBounded extends Capability[Random, Long, Nothing, Long]
158
object NextPrintableChar extends Capability[Random, Any, Nothing, Char]
159
object NextString extends Capability[Random, Int, Nothing, String]
160
object NextUUID extends Capability[Random, Any, Nothing, UUID]
161
object Shuffle extends Capability[Random, List[Any], Nothing, List[Any]]
162
163
// Expectation builders (examples)
164
def nextBoolean: Expectation[Random]
165
def nextInt: Expectation[Random]
166
def nextIntBetween(min: Int, max: Int): Expectation[Random]
167
def nextString(length: Int): Expectation[Random]
168
}
169
170
/**
171
* Mock implementation of ZIO System service
172
*/
173
object MockSystem extends Mock[System] {
174
// System capabilities
175
object Property extends Capability[System, String, Throwable, Option[String]]
176
object PropertyOrElse extends Capability[System, (String, String), Throwable, String]
177
object PropertyOrOption extends Capability[System, (String, Option[String]), Throwable, Option[String]]
178
object Env extends Capability[System, String, SecurityException, Option[String]]
179
object EnvOrElse extends Capability[System, (String, String), SecurityException, String]
180
object EnvOrOption extends Capability[System, (String, Option[String]), SecurityException, Option[String]]
181
object LineSeparator extends Capability[System, Any, Nothing, String]
182
183
// Expectation builders
184
def property(name: String): Expectation[System]
185
def env(name: String): Expectation[System]
186
def lineSeparator: Expectation[System]
187
}
188
```
189
190
### Expectation Creation and Configuration
191
192
Building expectations for mock method calls.
193
194
```scala { .api }
195
/**
196
* Create expectation that method will be called and return specified result
197
*/
198
def expect[R, I, E, A](capability: Capability[R, I, E, A])(assertion: Assertion[I], result: Result[E, A]): Expectation[R]
199
200
/**
201
* Create expectation with input matching
202
*/
203
def expect[R, I, E, A](capability: Capability[R, I, E, A])(input: I, result: Result[E, A]): Expectation[R]
204
205
/**
206
* Create expectation with input assertion and effectful result
207
*/
208
def expectM[R, I, E, A](capability: Capability[R, I, E, A])(assertion: Assertion[I], result: ZIO[Any, E, A]): Expectation[R]
209
```
210
211
**Expectation Modifiers:**
212
213
```scala { .api }
214
// Repetition control
215
expectation.repeats(1 to 3) // Called 1-3 times
216
expectation.repeats(Range.atLeast(2)) // Called at least 2 times
217
expectation.repeats(Range.exactly(1)) // Called exactly once
218
219
// Optional calls
220
expectation.optional // May or may not be called
221
222
// Logical combination
223
expectation1 && expectation2 // Both must be satisfied
224
expectation1 || expectation2 // Either can be satisfied
225
```
226
227
**Usage Examples:**
228
229
```scala
230
import zio.test.mock._
231
import zio.test.mock.Expectation._
232
233
// Simple mock expectation
234
val consoleMock = MockConsole.putStrLn(equalTo("Hello")) returns unit
235
236
// Multiple expectations
237
val clockMock =
238
MockClock.currentTime(equalTo(TimeUnit.MILLISECONDS)) returns value(1234567890L) &&
239
MockClock.sleep(equalTo(1.second)) returns unit
240
241
// Repeated calls
242
val randomMock = MockRandom.nextInt returns value(42) repeats (1 to 5)
243
244
// Complex assertions
245
val systemMock = MockSystem.property(startsWith("java.")) returns value(Some("value"))
246
```
247
248
### Mock Environment Creation
249
250
Creating mock environments for testing.
251
252
```scala { .api }
253
/**
254
* Create a mock environment layer from expectations
255
*/
256
def expectation[R: Tag](expectations: Expectation[R]*): ULayer[R]
257
258
/**
259
* Run test with mock environment
260
*/
261
def withMock[R: Tag, E, A](expectations: Expectation[R]*)(test: ZIO[R, E, A]): ZIO[Any, E, A]
262
```
263
264
**Usage Examples:**
265
266
```scala
267
import zio.test._
268
import zio.test.mock._
269
270
// Test with mock console
271
testM("greeting program") {
272
val mock = MockConsole.putStrLn(equalTo("Hello, World!")) returns unit
273
274
val program = putStrLn("Hello, World!")
275
276
program.provideLayer(MockConsole.expectation(mock))
277
}
278
279
// Multiple service mocks
280
testM("complex program") {
281
val consoleMock = MockConsole.putStrLn(anything) returns unit
282
val clockMock = MockClock.currentTime(anything) returns value(System.currentTimeMillis())
283
val randomMock = MockRandom.nextInt returns value(42)
284
285
val program = for {
286
time <- currentTime(TimeUnit.MILLISECONDS)
287
rand <- nextInt
288
_ <- putStrLn(s"Time: $time, Random: $rand")
289
} yield ()
290
291
program.provideLayer(
292
MockConsole.expectation(consoleMock) ++
293
MockClock.expectation(clockMock) ++
294
MockRandom.expectation(randomMock)
295
)
296
}
297
```
298
299
### Custom Mock Creation
300
301
Creating mocks for your own services.
302
303
```scala { .api }
304
/**
305
* Macro for generating mock implementations of services
306
*/
307
@mockable
308
trait UserService {
309
def getUser(id: String): IO[UserError, User]
310
def createUser(name: String, email: String): IO[UserError, User]
311
def deleteUser(id: String): IO[UserError, Unit]
312
}
313
314
// Generated mock object
315
object MockUserService extends Mock[UserService] {
316
object GetUser extends Capability[UserService, String, UserError, User]
317
object CreateUser extends Capability[UserService, (String, String), UserError, User]
318
object DeleteUser extends Capability[UserService, String, UserError, Unit]
319
320
def getUser(id: String): Expectation[UserService]
321
def createUser(name: String, email: String): Expectation[UserService]
322
def deleteUser(id: String): Expectation[UserService]
323
}
324
```
325
326
**Custom Mock Usage:**
327
328
```scala
329
// Test user repository with mock service
330
testM("user repository test") {
331
val user = User("123", "John", "john@example.com")
332
333
val userServiceMock =
334
MockUserService.getUser(equalTo("123")) returns value(user) &&
335
MockUserService.createUser(equalTo("Jane"), equalTo("jane@example.com")) returns value(User("124", "Jane", "jane@example.com"))
336
337
val repository = new UserRepository()
338
339
for {
340
retrieved <- repository.findUser("123")
341
created <- repository.addUser("Jane", "jane@example.com")
342
} yield assert(retrieved)(equalTo(user)) && assert(created.name)(equalTo("Jane"))
343
}.provideLayer(MockUserService.expectation(userServiceMock))
344
```
345
346
### Proxy and Internal Classes
347
348
Internal mock framework classes for advanced usage.
349
350
```scala { .api }
351
/**
352
* Proxy interface for mock implementations
353
*/
354
trait Proxy[M] {
355
def invoke[I, E, A](capability: Capability[M, I, E, A], input: I): ZIO[Any, E, A]
356
}
357
358
/**
359
* Mock state management
360
*/
361
object MockState {
362
def make: UIO[Ref[MockState]]
363
def checkUnmatched: ZIO[Any, UnmatchedExpectations, Unit]
364
def satisfied: UIO[Boolean]
365
}
366
367
/**
368
* Mock exceptions for expectation violations
369
*/
370
sealed trait MockException extends Exception
371
372
case class UnexpectedCall[I](capability: String, input: I) extends MockException
373
case class InvalidCall[I](capability: String, input: I, assertion: String) extends MockException
374
case class UnmatchedExpectations(expectations: List[String]) extends MockException
375
```
376
377
## Types
378
379
### Core Mock Types
380
381
```scala { .api }
382
/**
383
* Base mock trait
384
*/
385
trait Mock[R] {
386
type Environment = R
387
}
388
389
/**
390
* Test expectation for service method calls
391
*/
392
abstract class Expectation[R]
393
394
/**
395
* Service capability that can be mocked
396
*/
397
sealed trait Capability[R, I, E, A]
398
399
/**
400
* Result of mock method call
401
*/
402
sealed trait Result[+E, +A]
403
```
404
405
### Mock State Types
406
407
```scala { .api }
408
/**
409
* Internal state of mock system
410
*/
411
final case class MockState(
412
expectations: List[ExpectationState],
413
calls: List[CallLog]
414
)
415
416
/**
417
* State of individual expectation
418
*/
419
sealed trait ExpectationState
420
case class Pending(expectation: Expectation[Any]) extends ExpectationState
421
case class Matched(expectation: Expectation[Any], callCount: Int) extends ExpectationState
422
423
/**
424
* Log of mock method calls
425
*/
426
case class CallLog(
427
capability: String,
428
input: Any,
429
result: Either[Throwable, Any]
430
)
431
```
432
433
### Range Types for Repetition
434
435
```scala { .api }
436
/**
437
* Range specification for expectation repetition
438
*/
439
sealed trait Range
440
object Range {
441
def exactly(n: Int): Range
442
def atLeast(n: Int): Range
443
def atMost(n: Int): Range
444
def between(min: Int, max: Int): Range
445
}
446
```