0
# Test Environment
1
2
Controllable test services that replace ZIO's standard environment for deterministic testing.
3
4
## Capabilities
5
6
### TestClock
7
8
Controllable clock service for testing time-dependent operations.
9
10
```scala { .api }
11
/**
12
* Test clock service that provides deterministic time control
13
*/
14
trait TestClock extends Clock {
15
/** Advances the clock by specified duration */
16
def adjust(duration: Duration): UIO[Unit]
17
18
/** Sets clock to specific date and time */
19
def setDateTime(dateTime: OffsetDateTime): UIO[Unit]
20
21
/** Sets clock to specific time offset */
22
def setTime(duration: Duration): UIO[Unit]
23
24
/** Sets time zone for the clock */
25
def setTimeZone(zone: ZoneId): UIO[Unit]
26
27
/** Gets list of pending sleep operations */
28
def sleeps: UIO[List[Duration]]
29
30
/** Gets current time zone */
31
def timeZone: UIO[ZoneId]
32
}
33
34
object TestClock {
35
/** Access TestClock service from environment */
36
def adjust(duration: Duration): URIO[TestClock, Unit]
37
def setDateTime(dateTime: OffsetDateTime): URIO[TestClock, Unit]
38
def setTime(duration: Duration): URIO[TestClock, Unit]
39
def sleeps: URIO[TestClock, List[Duration]]
40
}
41
```
42
43
**Usage Examples:**
44
45
```scala
46
import zio.test.environment.TestClock
47
import zio.duration._
48
49
testM("time-based operation") {
50
for {
51
fiber <- someTTimedOperation.fork
52
_ <- TestClock.adjust(1.hour)
53
result <- fiber.join
54
} yield assert(result)(isSuccess)
55
}
56
57
testM("timeout behavior") {
58
for {
59
_ <- TestClock.setTime(0.seconds)
60
fiber <- longRunningTask.timeout(30.seconds).fork
61
_ <- TestClock.adjust(31.seconds)
62
result <- fiber.join
63
} yield assert(result)(isNone)
64
}
65
```
66
67
### TestConsole
68
69
Controllable console service for testing input/output operations.
70
71
```scala { .api }
72
/**
73
* Test console service that captures output and provides input
74
*/
75
trait TestConsole extends Console {
76
/** Clears the input buffer */
77
def clearInput: UIO[Unit]
78
79
/** Clears the output buffer */
80
def clearOutput: UIO[Unit]
81
82
/** Clears the error output buffer */
83
def clearOutputErr: UIO[Unit]
84
85
/** Adds input lines to be read */
86
def feedLines(lines: String*): UIO[Unit]
87
88
/** Gets all output lines written */
89
def output: UIO[Vector[String]]
90
91
/** Gets all error output lines written */
92
def outputErr: UIO[Vector[String]]
93
}
94
95
object TestConsole {
96
/** Access TestConsole service from environment */
97
def clearInput: URIO[TestConsole, Unit]
98
def clearOutput: URIO[TestConsole, Unit]
99
def feedLines(lines: String*): URIO[TestConsole, Unit]
100
def output: URIO[TestConsole, Vector[String]]
101
def outputErr: URIO[TestConsole, Vector[String]]
102
}
103
```
104
105
**Usage Examples:**
106
107
```scala
108
import zio.test.environment.TestConsole
109
import zio.console._
110
111
testM("console interaction") {
112
for {
113
_ <- TestConsole.feedLines("user input", "more input")
114
_ <- myInteractiveProgram
115
output <- TestConsole.output
116
} yield assert(output)(contains("Welcome!"))
117
}
118
119
testM("error output") {
120
for {
121
_ <- programThatPrintsErrors
122
errs <- TestConsole.outputErr
123
} yield assert(errs)(isNonEmpty)
124
}
125
```
126
127
### TestRandom
128
129
Controllable random service for deterministic testing.
130
131
```scala { .api }
132
/**
133
* Test random service that provides controllable randomness
134
*/
135
trait TestRandom extends Random {
136
/** Clears boolean values buffer */
137
def clearBooleans: UIO[Unit]
138
139
/** Clears byte array buffer */
140
def clearBytes: UIO[Unit]
141
142
/** Clears character values buffer */
143
def clearChars: UIO[Unit]
144
145
/** Clears double values buffer */
146
def clearDoubles: UIO[Unit]
147
148
/** Clears float values buffer */
149
def clearFloats: UIO[Unit]
150
151
/** Clears integer values buffer */
152
def clearInts: UIO[Unit]
153
154
/** Clears long values buffer */
155
def clearLongs: UIO[Unit]
156
157
/** Clears string values buffer */
158
def clearStrings: UIO[Unit]
159
160
/** Adds boolean values to be returned */
161
def feedBooleans(booleans: Boolean*): UIO[Unit]
162
163
/** Adds byte arrays to be returned */
164
def feedBytes(bytes: Chunk[Byte]*): UIO[Unit]
165
166
/** Adds character values to be returned */
167
def feedChars(chars: Char*): UIO[Unit]
168
169
/** Adds double values to be returned */
170
def feedDoubles(doubles: Double*): UIO[Unit]
171
172
/** Adds float values to be returned */
173
def feedFloats(floats: Float*): UIO[Unit]
174
175
/** Adds integer values to be returned */
176
def feedInts(ints: Int*): UIO[Unit]
177
178
/** Adds long values to be returned */
179
def feedLongs(longs: Long*): UIO[Unit]
180
181
/** Adds string values to be returned */
182
def feedStrings(strings: String*): UIO[Unit]
183
184
/** Sets the random seed */
185
def setSeed(seed: Long): UIO[Unit]
186
}
187
188
object TestRandom {
189
/** Access TestRandom service from environment */
190
def feedInts(ints: Int*): URIO[TestRandom, Unit]
191
def feedDoubles(doubles: Double*): URIO[TestRandom, Unit]
192
def feedBooleans(booleans: Boolean*): URIO[TestRandom, Unit]
193
def setSeed(seed: Long): URIO[TestRandom, Unit]
194
}
195
```
196
197
**Usage Examples:**
198
199
```scala
200
import zio.test.environment.TestRandom
201
import zio.random._
202
203
testM("deterministic randomness") {
204
for {
205
_ <- TestRandom.feedInts(1, 2, 3, 4, 5)
206
result <- randomGameSimulation
207
} yield assert(result.winner)(equalTo("Player1"))
208
}
209
210
testM("seeded randomness") {
211
for {
212
_ <- TestRandom.setSeed(12345L)
213
result1 <- randomOperation
214
_ <- TestRandom.setSeed(12345L)
215
result2 <- randomOperation
216
} yield assert(result1)(equalTo(result2))
217
}
218
```
219
220
### TestSystem
221
222
Controllable system service for testing environment variables and system properties.
223
224
```scala { .api }
225
/**
226
* Test system service that provides controllable system state
227
*/
228
trait TestSystem extends System {
229
/** Clears all environment variables */
230
def clearEnv: UIO[Unit]
231
232
/** Clears all system properties */
233
def clearProperties: UIO[Unit]
234
235
/** Sets environment variable */
236
def putEnv(name: String, value: String): UIO[Unit]
237
238
/** Sets system property */
239
def putProperty(name: String, value: String): UIO[Unit]
240
241
/** Sets line separator */
242
def setLineSeparator(lineSeparator: String): UIO[Unit]
243
}
244
245
object TestSystem {
246
/** Access TestSystem service from environment */
247
def clearEnv: URIO[TestSystem, Unit]
248
def putEnv(name: String, value: String): URIO[TestSystem, Unit]
249
def putProperty(name: String, value: String): URIO[TestSystem, Unit]
250
}
251
```
252
253
**Usage Examples:**
254
255
```scala
256
import zio.test.environment.TestSystem
257
import zio.system._
258
259
testM("environment variables") {
260
for {
261
_ <- TestSystem.putEnv("TEST_MODE", "enabled")
262
_ <- TestSystem.putEnv("API_KEY", "test-key-123")
263
result <- programThatReadsEnvVars
264
} yield assert(result)(isSuccess)
265
}
266
267
testM("system properties") {
268
for {
269
_ <- TestSystem.putProperty("user.name", "testuser")
270
result <- programThatReadsSystemProps
271
} yield assert(result.username)(equalTo("testuser"))
272
}
273
```
274
275
### TestEnvironment
276
277
Complete test environment combining all test services.
278
279
```scala { .api }
280
/**
281
* Complete test environment with all controllable services
282
*/
283
type TestEnvironment = TestClock with TestConsole with TestRandom with TestSystem
284
285
/**
286
* Complete ZIO test environment
287
*/
288
type ZTestEnv = TestClock with TestConsole with TestRandom with TestSystem
289
290
object TestEnvironment {
291
/** Live layer providing test environment */
292
val live: Layer[Nothing, TestEnvironment]
293
}
294
295
/** Default test environment layer */
296
val testEnvironment: Layer[Nothing, TestEnvironment]
297
298
/** Managed test environment resource */
299
val testEnvironmentManaged: TaskManaged[TestEnvironment]
300
```
301
302
### Environment Services Access
303
304
Service objects for accessing test environment components.
305
306
```scala { .api }
307
object Annotations {
308
trait Service {
309
def annotate[V](key: TestAnnotation[V], value: V): UIO[Unit]
310
def get[V](key: TestAnnotation[V]): UIO[V]
311
def supervisedFibers: UIO[SortedSet[Fiber.Runtime[Any, Any]]]
312
}
313
314
def annotate[V](key: TestAnnotation[V], value: V): URIO[Annotations, Unit]
315
def get[V](key: TestAnnotation[V]): URIO[Annotations, V]
316
val live: Layer[Nothing, Annotations]
317
}
318
319
object Sized {
320
trait Service {
321
def size: UIO[Int]
322
def withSize[R, E, A](size: Int)(zio: ZIO[R, E, A]): ZIO[R, E, A]
323
}
324
325
def live(size: Int): Layer[Nothing, Sized]
326
def size: URIO[Sized, Int]
327
def withSize[R <: Sized, E, A](size: Int)(zio: ZIO[R, E, A]): ZIO[R, E, A]
328
}
329
330
object TestConfig {
331
trait Service {
332
def repeats: Int
333
def retries: Int
334
def samples: Int
335
def shrinks: Int
336
}
337
338
def live(repeats: Int, retries: Int, samples: Int, shrinks: Int): ZLayer[Any, Nothing, TestConfig]
339
val repeats: URIO[TestConfig, Int]
340
val retries: URIO[TestConfig, Int]
341
val samples: URIO[TestConfig, Int]
342
val shrinks: URIO[TestConfig, Int]
343
}
344
```
345
346
## Complete Example
347
348
```scala
349
import zio.test._
350
import zio.test.environment._
351
import zio.duration._
352
353
object TestEnvironmentExample extends DefaultRunnableSpec {
354
def spec = suite("Test Environment Example")(
355
356
testM("clock control") {
357
for {
358
start <- clock.nanoTime
359
_ <- TestClock.setTime(1.hour)
360
end <- clock.nanoTime
361
diff = end - start
362
} yield assert(diff)(isGreaterThan(3600000000000L)) // 1 hour in nanos
363
},
364
365
testM("console interaction") {
366
for {
367
_ <- TestConsole.feedLines("Alice", "25")
368
_ <- putStrLn("Enter name:")
369
name <- getStrLn
370
_ <- putStrLn("Enter age:")
371
age <- getStrLn
372
output <- TestConsole.output
373
} yield assert(output)(contains("Enter name:")) &&
374
assert(name)(equalTo("Alice")) &&
375
assert(age)(equalTo("25"))
376
},
377
378
testM("deterministic random") {
379
for {
380
_ <- TestRandom.feedInts(42, 17, 99)
381
int1 <- nextInt
382
int2 <- nextInt
383
int3 <- nextInt
384
} yield assert(int1)(equalTo(42)) &&
385
assert(int2)(equalTo(17)) &&
386
assert(int3)(equalTo(99))
387
},
388
389
testM("system environment") {
390
for {
391
_ <- TestSystem.putEnv("HOME", "/test/home")
392
_ <- TestSystem.putProperty("user.name", "testuser")
393
home <- system.env("HOME")
394
user <- system.property("user.name")
395
} yield assert(home)(isSome(equalTo("/test/home"))) &&
396
assert(user)(isSome(equalTo("testuser")))
397
}
398
)
399
}
400
```