or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdcore-dsl.mdindex.mdmock-testing.mdproperty-testing.mdspecifications.mdtest-aspects.mdtest-environment.md

test-environment.mddocs/

0

# Test Environment

1

2

Testable versions of ZIO services enabling deterministic testing of time, console, randomness, and system properties. The test environment provides controllable, predictable implementations of environmental services.

3

4

## Capabilities

5

6

### TestClock Service

7

8

Deterministic time control for testing time-dependent operations.

9

10

```scala { .api }

11

/**

12

* TestClock enables deterministic testing of time-dependent effects.

13

* Instead of waiting for real time, effects are scheduled and executed

14

* when the test clock is advanced.

15

*/

16

object TestClock {

17

/**

18

* Advance the test clock by the specified duration

19

* @param duration - Time to advance

20

* @returns Effect that advances time and runs scheduled effects

21

*/

22

def adjust(duration: Duration): URIO[TestClock, Unit]

23

24

/**

25

* Set the test clock to a specific time

26

* @param duration - Absolute time since epoch

27

* @returns Effect that sets time and runs scheduled effects

28

*/

29

def setTime(duration: Duration): URIO[TestClock, Unit]

30

31

/**

32

* Set the test clock to a specific date/time

33

* @param dateTime - Specific date and time

34

* @returns Effect that sets time and runs scheduled effects

35

*/

36

def setDateTime(dateTime: OffsetDateTime): URIO[TestClock, Unit]

37

38

/**

39

* Set the time zone for the test clock

40

* @param zone - Time zone to use

41

* @returns Effect that changes the time zone

42

*/

43

def setTimeZone(zone: ZoneId): URIO[TestClock, Unit]

44

45

/**

46

* Get list of scheduled sleep durations

47

* @returns Current sleep schedule

48

*/

49

val sleeps: ZIO[TestClock, Nothing, List[Duration]]

50

51

/**

52

* Get current time zone

53

* @returns Current time zone setting

54

*/

55

val timeZone: URIO[TestClock, ZoneId]

56

57

/**

58

* Save current clock state for later restoration

59

* @returns Effect that when run will restore the clock state

60

*/

61

val save: ZIO[TestClock, Nothing, UIO[Unit]]

62

}

63

```

64

65

**Usage Examples:**

66

67

```scala

68

import zio.test.environment.TestClock

69

import zio.duration._

70

71

// Test timeout behavior

72

testM("operation times out") {

73

for {

74

fiber <- longRunningOperation.timeout(1.minute).fork

75

_ <- TestClock.adjust(1.minute)

76

result <- fiber.join

77

} yield assert(result)(isNone)

78

}

79

80

// Test recurring operations

81

testM("recurring task") {

82

for {

83

queue <- Queue.unbounded[Unit]

84

_ <- queue.offer(()).delay(30.seconds).forever.fork

85

_ <- TestClock.adjust(30.seconds)

86

item1 <- queue.take

87

_ <- TestClock.adjust(30.seconds)

88

item2 <- queue.take

89

} yield assert((item1, item2))(equalTo(((), ())))

90

}

91

```

92

93

### TestConsole Service

94

95

Console input/output testing with controllable input and output capture.

96

97

```scala { .api }

98

/**

99

* TestConsole provides testable console operations with input feeding

100

* and output capture capabilities.

101

*/

102

object TestConsole {

103

/**

104

* Add lines to the input buffer for console reads

105

* @param lines - Lines to add to input buffer

106

* @returns Effect that feeds the input buffer

107

*/

108

def feedLines(lines: String*): URIO[TestConsole, Unit]

109

110

/**

111

* Get all output written to console

112

* @returns Vector of all output strings

113

*/

114

val output: ZIO[TestConsole, Nothing, Vector[String]]

115

116

/**

117

* Get all error output written to console

118

* @returns Vector of all error output strings

119

*/

120

val outputErr: ZIO[TestConsole, Nothing, Vector[String]]

121

122

/**

123

* Clear the input buffer

124

* @returns Effect that clears input

125

*/

126

val clearInput: URIO[TestConsole, Unit]

127

128

/**

129

* Clear the output buffer

130

* @returns Effect that clears output

131

*/

132

val clearOutput: URIO[TestConsole, Unit]

133

134

/**

135

* Run effect with debug mode (output to real console too)

136

* @param zio - Effect to run with debug output

137

* @returns Effect with debug console output enabled

138

*/

139

def debug[R <: TestConsole, E, A](zio: ZIO[R, E, A]): ZIO[R, E, A]

140

141

/**

142

* Run effect with silent mode (no real console output)

143

* @param zio - Effect to run without console output

144

* @returns Effect with console output suppressed

145

*/

146

def silent[R <: TestConsole, E, A](zio: ZIO[R, E, A]): ZIO[R, E, A]

147

148

/**

149

* Save current console state for later restoration

150

* @returns Effect that when run will restore console state

151

*/

152

val save: ZIO[TestConsole, Nothing, UIO[Unit]]

153

}

154

```

155

156

**Usage Examples:**

157

158

```scala

159

import zio.console._

160

import zio.test.environment.TestConsole

161

162

// Test interactive program

163

testM("interactive greeting") {

164

val program = for {

165

_ <- putStrLn("What's your name?")

166

name <- getStrLn

167

_ <- putStrLn(s"Hello, $name!")

168

} yield ()

169

170

for {

171

_ <- TestConsole.feedLines("Alice")

172

_ <- program

173

output <- TestConsole.output

174

} yield assert(output)(equalTo(Vector("What's your name?\n", "Hello, Alice!\n")))

175

}

176

177

// Test error output

178

testM("error logging") {

179

for {

180

_ <- putStrLnErr("Error occurred")

181

errorOut <- TestConsole.outputErr

182

} yield assert(errorOut)(contains("Error occurred\n"))

183

}

184

```

185

186

### TestRandom Service

187

188

Deterministic random number generation with controllable sequences.

189

190

```scala { .api }

191

/**

192

* TestRandom provides deterministic random number generation.

193

* It operates in two modes: pseudo-random (with seeds) and

194

* buffered (with pre-fed values).

195

*/

196

object TestRandom {

197

// Feeding buffers with predetermined values

198

def feedInts(ints: Int*): URIO[TestRandom, Unit]

199

def feedLongs(longs: Long*): URIO[TestRandom, Unit]

200

def feedDoubles(doubles: Double*): URIO[TestRandom, Unit]

201

def feedFloats(floats: Float*): URIO[TestRandom, Unit]

202

def feedBooleans(booleans: Boolean*): URIO[TestRandom, Unit]

203

def feedChars(chars: Char*): URIO[TestRandom, Unit]

204

def feedStrings(strings: String*): URIO[TestRandom, Unit]

205

def feedBytes(bytes: Chunk[Byte]*): URIO[TestRandom, Unit]

206

def feedUUIDs(UUIDs: UUID*): URIO[TestRandom, Unit]

207

208

// Clearing buffers

209

val clearInts: URIO[TestRandom, Unit]

210

val clearLongs: URIO[TestRandom, Unit]

211

val clearDoubles: URIO[TestRandom, Unit]

212

val clearFloats: URIO[TestRandom, Unit]

213

val clearBooleans: URIO[TestRandom, Unit]

214

val clearChars: URIO[TestRandom, Unit]

215

val clearStrings: URIO[TestRandom, Unit]

216

val clearBytes: URIO[TestRandom, Unit]

217

val clearUUIDs: URIO[TestRandom, Unit]

218

219

/**

220

* Set the random seed for pseudo-random generation

221

* @param seed - Seed value for random number generator

222

* @returns Effect that sets the seed

223

*/

224

def setSeed(seed: Long): URIO[TestRandom, Unit]

225

226

/**

227

* Get the current random seed

228

* @returns Current seed value

229

*/

230

val getSeed: URIO[TestRandom, Long]

231

232

/**

233

* Save current random state for later restoration

234

* @returns Effect that when run will restore random state

235

*/

236

val save: ZIO[TestRandom, Nothing, UIO[Unit]]

237

}

238

```

239

240

**Usage Examples:**

241

242

```scala

243

import zio.random._

244

import zio.test.environment.TestRandom

245

246

// Test with predetermined values

247

testM("dice roll sequence") {

248

for {

249

_ <- TestRandom.feedInts(1, 6, 3, 4)

250

roll1 <- nextIntBounded(6).map(_ + 1)

251

roll2 <- nextIntBounded(6).map(_ + 1)

252

roll3 <- nextIntBounded(6).map(_ + 1)

253

roll4 <- nextIntBounded(6).map(_ + 1)

254

} yield assert((roll1, roll2, roll3, roll4))(equalTo((1, 6, 3, 4)))

255

}

256

257

// Test with deterministic seed

258

testM("reproducible randomness") {

259

for {

260

_ <- TestRandom.setSeed(42L)

261

value1 <- nextInt

262

_ <- TestRandom.setSeed(42L)

263

value2 <- nextInt

264

} yield assert(value1)(equalTo(value2))

265

}

266

```

267

268

### TestSystem Service

269

270

System properties and environment variable testing with controlled mappings.

271

272

```scala { .api }

273

/**

274

* TestSystem provides testable system property and environment variable

275

* access without affecting the real system environment.

276

*/

277

object TestSystem {

278

/**

279

* Set a system property in the test environment

280

* @param name - Property name

281

* @param value - Property value

282

* @returns Effect that sets the property

283

*/

284

def putProperty(name: String, value: String): URIO[TestSystem, Unit]

285

286

/**

287

* Set an environment variable in the test environment

288

* @param name - Variable name

289

* @param value - Variable value

290

* @returns Effect that sets the variable

291

*/

292

def putEnv(name: String, value: String): URIO[TestSystem, Unit]

293

294

/**

295

* Set the line separator for the test environment

296

* @param lineSep - Line separator string

297

* @returns Effect that sets the line separator

298

*/

299

def setLineSeparator(lineSep: String): URIO[TestSystem, Unit]

300

301

/**

302

* Clear a system property from the test environment

303

* @param prop - Property name to clear

304

* @returns Effect that removes the property

305

*/

306

def clearProperty(prop: String): URIO[TestSystem, Unit]

307

308

/**

309

* Clear an environment variable from the test environment

310

* @param variable - Variable name to clear

311

* @returns Effect that removes the variable

312

*/

313

def clearEnv(variable: String): URIO[TestSystem, Unit]

314

315

/**

316

* Save current system state for later restoration

317

* @returns Effect that when run will restore system state

318

*/

319

val save: ZIO[TestSystem, Nothing, UIO[Unit]]

320

}

321

```

322

323

**Usage Examples:**

324

325

```scala

326

import zio.system._

327

import zio.test.environment.TestSystem

328

329

// Test configuration loading

330

testM("configuration from environment") {

331

for {

332

_ <- TestSystem.putEnv("APP_MODE", "test")

333

_ <- TestSystem.putProperty("app.timeout", "30")

334

mode <- env("APP_MODE")

335

timeout <- property("app.timeout")

336

} yield assert(mode)(isSome(equalTo("test"))) &&

337

assert(timeout)(isSome(equalTo("30")))

338

}

339

340

// Test different line separators

341

testM("file writing with line separator") {

342

for {

343

_ <- TestSystem.setLineSeparator("\r\n")

344

lines <- system.lineSeparator

345

} yield assert(lines)(equalTo("\r\n"))

346

}

347

```

348

349

### Live Service

350

351

Access to real environment from within test environment.

352

353

```scala { .api }

354

/**

355

* Live service provides access to the real environment for operations

356

* that need actual system resources (like timing out tests).

357

*/

358

object Live {

359

/**

360

* Run an effect with the live environment instead of test environment

361

* @param zio - Effect that needs live environment

362

* @returns Effect that runs with real environment

363

*/

364

def live[E, A](zio: ZIO[ZEnv, E, A]): ZIO[Live, E, A]

365

366

/**

367

* Transform an effect using live environment while keeping test environment for the effect itself

368

* @param zio - Effect to transform

369

* @param f - Transformation function that needs live environment

370

* @returns Transformed effect

371

*/

372

def withLive[R, E, E1, A, B](

373

zio: ZIO[R, E, A]

374

)(f: IO[E, A] => ZIO[ZEnv, E1, B]): ZIO[R with Live, E1, B]

375

}

376

```

377

378

**Usage Examples:**

379

380

```scala

381

import zio.test.environment.Live

382

import zio.clock._

383

384

// Get real time while using test environment

385

testM("real time logging") {

386

for {

387

realTime <- Live.live(currentTime(TimeUnit.MILLISECONDS))

388

_ <- logMessage(s"Test started at $realTime")

389

} yield assertCompletes

390

}

391

392

// Timeout using real time

393

testM("real timeout") {

394

Live.withLive(longRunningTest)(_.timeout(5.seconds))

395

}

396

```

397

398

### Environment Composition

399

400

Combining test services into complete test environments.

401

402

```scala { .api }

403

/**

404

* Combined test environment with all test services

405

*/

406

type TestEnvironment = TestClock with TestConsole with TestRandom with TestSystem with Annotations with Live

407

408

/**

409

* Type alias for ZIO test environment

410

*/

411

type ZTestEnv = TestClock with TestConsole with TestRandom with TestSystem

412

413

/**

414

* Layer that provides complete test environment

415

*/

416

val testEnvironment: Layer[Nothing, TestEnvironment]

417

418

/**

419

* Layer that provides live environment

420

*/

421

val liveEnvironment: Layer[Nothing, ZEnv]

422

```

423

424

**Usage Examples:**

425

426

```scala

427

// Using the complete test environment

428

object MySpec extends DefaultRunnableSpec {

429

def spec = suite("my tests")(

430

testM("uses all test services") {

431

for {

432

_ <- TestClock.adjust(1.hour)

433

_ <- TestConsole.feedLines("input")

434

_ <- TestRandom.feedInts(42)

435

_ <- TestSystem.putEnv("MODE", "test")

436

// Test logic using all services

437

} yield assertCompletes

438

}

439

)

440

}

441

442

// Custom environment layer

443

val myTestEnv = testEnvironment >>> myCustomServices

444

445

myProgram.provideLayer(myTestEnv)

446

```

447

448

## Types

449

450

### Service Types

451

452

```scala { .api }

453

// Individual test services

454

type TestClock = Has[TestClock.Service]

455

type TestConsole = Has[TestConsole.Service]

456

type TestRandom = Has[TestRandom.Service]

457

type TestSystem = Has[TestSystem.Service]

458

type Live = Has[Live.Service]

459

460

// Service data structures

461

object TestClock {

462

final case class Data(

463

duration: Duration,

464

sleeps: List[(Duration, Promise[Nothing, Unit])],

465

timeZone: ZoneId

466

)

467

}

468

469

object TestConsole {

470

final case class Data(

471

input: List[String] = List.empty,

472

output: Vector[String] = Vector.empty,

473

errOutput: Vector[String] = Vector.empty

474

)

475

}

476

477

object TestRandom {

478

final case class Data(

479

seed1: Int,

480

seed2: Int,

481

nextNextGaussians: Queue[Double] = Queue.empty

482

)

483

484

final case class Buffer(

485

booleans: List[Boolean] = List.empty,

486

bytes: List[Chunk[Byte]] = List.empty,

487

chars: List[Char] = List.empty,

488

doubles: List[Double] = List.empty,

489

floats: List[Float] = List.empty,

490

integers: List[Int] = List.empty,

491

longs: List[Long] = List.empty,

492

strings: List[String] = List.empty,

493

UUIDs: List[UUID] = List.empty

494

)

495

}

496

497

object TestSystem {

498

final case class Data(

499

properties: Map[String, String] = Map.empty,

500

envs: Map[String, String] = Map.empty,

501

lineSeparator: String = "\n"

502

)

503

}

504

```

505

506

### Restorable Trait

507

508

```scala { .api }

509

/**

510

* Trait for services that can save and restore their state

511

*/

512

trait Restorable {

513

/**

514

* Save current state and return effect that restores it

515

*/

516

val save: UIO[UIO[Unit]]

517

}

518

```