or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdconfiguration.mdindex.mdproperty-testing.mdtest-aspects.mdtest-definition.mdtest-services.md

test-aspects.mddocs/

0

# Test Aspects

1

2

Cross-cutting concerns for test execution including timeouts, retries, parallelism, and platform-specific behavior.

3

4

## Capabilities

5

6

### TestAspect Trait

7

8

Core trait for aspects that modify test execution behavior.

9

10

```scala { .api }

11

/**

12

* A test aspect that can transform test specifications

13

* @tparam LowerR minimum environment requirement

14

* @tparam UpperR maximum environment requirement

15

* @tparam LowerE minimum error type

16

* @tparam UpperE maximum error type

17

*/

18

trait TestAspect[-LowerR, +UpperR, -LowerE, +UpperE] {

19

/**

20

* Apply this aspect to a test specification

21

* @param spec the test specification to transform

22

* @return transformed specification

23

*/

24

def apply[R >: LowerR <: UpperR, E >: LowerE <: UpperE](

25

spec: Spec[R, E]

26

): Spec[R, E]

27

28

/**

29

* Compose with another test aspect

30

* @param that other aspect to compose with

31

* @return composed aspect

32

*/

33

def @@[LowerR1 <: LowerR, UpperR1 >: UpperR, LowerE1 <: LowerE, UpperE1 >: UpperE](

34

that: TestAspect[LowerR1, UpperR1, LowerE1, UpperE1]

35

): TestAspect[LowerR1, UpperR1, LowerE1, UpperE1]

36

}

37

38

// Type aliases for common patterns

39

type TestAspectPoly = TestAspect[Nothing, Any, Nothing, Any]

40

type TestAspectAtLeastR[-R] = TestAspect[Nothing, R, Nothing, Any]

41

```

42

43

### Timing and Execution Control

44

45

Aspects for controlling test timing, timeouts, and execution behavior.

46

47

```scala { .api }

48

/**

49

* Apply timeout to test execution

50

* @param duration maximum duration before timeout

51

* @return aspect that times out tests exceeding duration

52

*/

53

def timeout(duration: Duration): TestAspectAtLeastR[Live]

54

55

/**

56

* Retry failing tests with exponential backoff

57

* @param n maximum number of retry attempts

58

* @return aspect that retries failed tests

59

*/

60

def retry(n: Int): TestAspectPoly

61

62

/**

63

* Retry tests until they succeed or max attempts reached

64

* Uses exponential backoff and jittering for better behavior

65

*/

66

val eventually: TestAspectAtLeastR[Live]

67

68

/**

69

* Mark tests as flaky (expected to fail intermittently)

70

* Flaky tests that fail are reported differently

71

*/

72

val flaky: TestAspectPoly

73

74

/**

75

* Mark test as non-flaky (opposite of flaky)

76

* Ensures test failures will fail the build

77

*/

78

val nonFlaky: TestAspectPoly

79

80

/**

81

* Mark tests to be ignored (not executed)

82

* Ignored tests are skipped but reported in results

83

*/

84

val ignore: TestAspectPoly

85

86

/**

87

* Repeat test execution N times

88

* @param n number of times to repeat the test

89

*/

90

def repeats(n: Int): TestAspectPoly

91

92

/**

93

* Execute tests with live clock instead of test clock

94

* Useful for tests that need real time progression

95

*/

96

val withLiveClock: TestAspectAtLeastR[Live]

97

98

/**

99

* Execute tests with live console instead of test console

100

* Useful for tests that need real console I/O

101

*/

102

val withLiveConsole: TestAspectAtLeastR[Live]

103

104

/**

105

* Execute tests with live random instead of test random

106

* Useful for tests that need non-deterministic randomness

107

*/

108

val withLiveRandom: TestAspectAtLeastR[Live]

109

110

/**

111

* Execute tests with live system instead of test system

112

* Useful for tests that need real system environment

113

*/

114

val withLiveSystem: TestAspectAtLeastR[Live]

115

```

116

117

**Usage Examples:**

118

119

```scala

120

import zio.test._

121

import zio.test.TestAspect._

122

123

// Timeout aspects

124

test("fast operation") {

125

ZIO.sleep(100.millis) *> assertTrue(true)

126

} @@ timeout(1.second)

127

128

suite("API Tests")(

129

test("get users") { /* test logic */ },

130

test("create user") { /* test logic */ }

131

) @@ timeout(30.seconds)

132

133

// Retry and flaky tests

134

test("external service call") {

135

// Might fail due to network issues

136

externalService.getData.map(data => assertTrue(data.nonEmpty))

137

} @@ eventually

138

139

test("timing sensitive operation") {

140

// Occasionally fails due to timing

141

timingSensitiveOperation

142

} @@ flaky @@ retry(3)

143

144

// Live service aspects

145

test("actual time measurement") {

146

for {

147

start <- Clock.currentTime(TimeUnit.MILLISECONDS)

148

_ <- ZIO.sleep(100.millis)

149

end <- Clock.currentTime(TimeUnit.MILLISECONDS)

150

} yield assertTrue(end - start >= 100)

151

} @@ withLiveClock

152

```

153

154

### Parallelism and Execution Strategy

155

156

Aspects controlling how tests are executed relative to each other.

157

158

```scala { .api }

159

/**

160

* Execute child tests in parallel

161

* Tests within the same suite run concurrently

162

*/

163

val parallel: TestAspectPoly

164

165

/**

166

* Execute child tests sequentially (default behavior)

167

* Tests run one after another in order

168

*/

169

val sequential: TestAspectPoly

170

171

/**

172

* Control parallelism level for test execution

173

* @param n maximum number of concurrent tests

174

*/

175

def parallelN(n: Int): TestAspectPoly

176

177

/**

178

* Execute each test in an isolated fiber

179

* Provides better isolation but more overhead

180

*/

181

val fibers: TestAspectPoly

182

183

/**

184

* Execute tests on a specific executor

185

* @param executor custom executor for test execution

186

*/

187

def executor(executor: Executor): TestAspectPoly

188

```

189

190

**Usage Examples:**

191

192

```scala

193

import zio.test._

194

import zio.test.TestAspect._

195

196

// Parallel execution

197

suite("Independent Tests")(

198

test("test A") { /* independent test */ },

199

test("test B") { /* independent test */ },

200

test("test C") { /* independent test */ }

201

) @@ parallel

202

203

// Sequential execution (explicit)

204

suite("Dependent Tests")(

205

test("setup") { /* setup state */ },

206

test("use setup") { /* depends on setup */ },

207

test("cleanup") { /* cleanup state */ }

208

) @@ sequential

209

210

// Limited parallelism

211

suite("Database Tests")(

212

test("query 1") { /* database query */ },

213

test("query 2") { /* database query */ },

214

test("query 3") { /* database query */ }

215

) @@ parallelN(2) // At most 2 concurrent queries

216

217

// Custom executor

218

suite("CPU Intensive Tests")(

219

test("heavy computation") { /* CPU bound test */ }

220

) @@ executor(Runtime.defaultExecutor)

221

```

222

223

### Platform and Environment Filtering

224

225

Aspects for controlling test execution based on platform or environment conditions.

226

227

```scala { .api }

228

/**

229

* Execute only on JVM platform

230

*/

231

val jvm: TestAspectPoly

232

233

/**

234

* Execute only on Scala.js platform

235

*/

236

val js: TestAspectPoly

237

238

/**

239

* Execute only on Scala Native platform

240

*/

241

val native: TestAspectPoly

242

243

/**

244

* Execute only on Unix/Linux platforms

245

*/

246

val unix: TestAspectPoly

247

248

/**

249

* Execute only on Windows platforms

250

*/

251

val windows: TestAspectPoly

252

253

/**

254

* Execute based on system property condition

255

* @param property system property name

256

* @param value expected property value

257

*/

258

def ifProp(property: String, value: String): TestAspectPoly

259

260

/**

261

* Execute based on environment variable condition

262

* @param variable environment variable name

263

* @param value expected variable value

264

*/

265

def ifEnv(variable: String, value: String): TestAspectPoly

266

267

/**

268

* Execute based on custom condition

269

* @param condition predicate determining execution

270

*/

271

def conditional(condition: Boolean): TestAspectPoly

272

```

273

274

**Usage Examples:**

275

276

```scala

277

import zio.test._

278

import zio.test.TestAspect._

279

280

// Platform-specific tests

281

suite("File System Tests")(

282

test("Unix file permissions") {

283

/* Unix-specific file operations */

284

} @@ unix,

285

286

test("Windows file paths") {

287

/* Windows-specific path handling */

288

} @@ windows,

289

290

test("Cross-platform file I/O") {

291

/* Works on all platforms */

292

}

293

)

294

295

// JavaScript-specific tests

296

suite("Browser Tests")(

297

test("DOM manipulation") {

298

/* Browser-specific functionality */

299

} @@ js,

300

301

test("Node.js file system") {

302

/* Node.js specific APIs */

303

} @@ js

304

)

305

306

// Conditional execution

307

suite("Integration Tests")(

308

test("database integration") {

309

/* Requires database */

310

} @@ ifEnv("DATABASE_URL", "test"),

311

312

test("debug mode features") {

313

/* Only in debug builds */

314

} @@ ifProp("debug", "true")

315

)

316

```

317

318

### Test Repetition and Sampling

319

320

Aspects for controlling how many times tests are executed.

321

322

```scala { .api }

323

/**

324

* Repeat test execution multiple times

325

* @param n number of repetitions

326

*/

327

def repeats(n: Int): TestAspectPoly

328

329

/**

330

* Configure number of samples for property-based tests

331

* @param n number of samples to generate and test

332

*/

333

def samples(n: Int): TestAspectPoly

334

335

/**

336

* Configure shrinking attempts for failed property tests

337

* @param n maximum shrinking attempts

338

*/

339

def shrinks(n: Int): TestAspectPoly

340

341

/**

342

* Set size parameter for generators

343

* @param n size value for Sized environment

344

*/

345

def size(n: Int): TestAspectPoly

346

347

/**

348

* Provide custom test configuration

349

* @param config test configuration parameters

350

*/

351

def config(config: TestConfig): TestAspectPoly

352

```

353

354

**Usage Examples:**

355

356

```scala

357

import zio.test._

358

import zio.test.TestAspect._

359

360

// Repeat tests for reliability

361

test("flaky network operation") {

362

networkCall.map(result => assertTrue(result.isSuccess))

363

} @@ repeats(10)

364

365

// Property test configuration

366

test("sorting properties") {

367

check(listOf(anyInt)) { list =>

368

val sorted = list.sorted

369

assertTrue(sorted.size == list.size)

370

}

371

} @@ samples(1000) @@ shrinks(100)

372

373

// Size control for generators

374

test("large data structures") {

375

check(listOf(anyString)) { largeList =>

376

assertTrue(largeList.forall(_.nonEmpty))

377

}

378

} @@ size(10000)

379

```

380

381

### Output and Logging Control

382

383

Aspects for controlling test output and logging behavior.

384

385

```scala { .api }

386

/**

387

* Suppress test output (silent execution)

388

*/

389

val silent: TestAspectPoly

390

391

/**

392

* Enable debug output with detailed information

393

*/

394

val debug: TestAspectPoly

395

396

/**

397

* Add custom annotations to test results

398

* @param annotations key-value annotations

399

*/

400

def annotate(annotations: TestAnnotation[_]*): TestAspectPoly

401

402

/**

403

* Tag tests with custom labels for filtering

404

* @param tags string tags for test organization

405

*/

406

def tag(tags: String*): TestAspectPoly

407

408

/**

409

* Execute with custom test logger

410

* @param logger custom logger implementation

411

*/

412

def withLogger(logger: TestLogger): TestAspectPoly

413

```

414

415

**Usage Examples:**

416

417

```scala

418

import zio.test._

419

import zio.test.TestAspect._

420

421

// Silent execution for performance tests

422

suite("Performance Tests")(

423

test("benchmark operation") { /* performance test */ }

424

) @@ silent

425

426

// Tagged tests for organization

427

suite("API Tests")(

428

test("user endpoints") { /* test */ } @@ tag("user", "api"),

429

test("admin endpoints") { /* test */ } @@ tag("admin", "api", "security")

430

)

431

432

// Custom annotations

433

test("database test") {

434

/* test logic */

435

} @@ annotate(

436

TestAnnotation.database("postgresql"),

437

TestAnnotation.timeout(30.seconds)

438

)

439

```

440

441

### Aspect Composition

442

443

Combining multiple aspects for complex test behavior.

444

445

```scala { .api }

446

/**

447

* Compose aspects using the @@ operator

448

* Aspects are applied in order from left to right

449

*/

450

val composedAspect: TestAspectPoly =

451

timeout(30.seconds) @@

452

parallel @@

453

eventually @@

454

tag("integration")

455

456

/**

457

* Conditional aspect application

458

* @param condition when to apply the aspect

459

* @param aspect aspect to apply conditionally

460

*/

461

def when(condition: Boolean)(aspect: TestAspectPoly): TestAspectPoly

462

```

463

464

**Usage Examples:**

465

466

```scala

467

import zio.test._

468

import zio.test.TestAspect._

469

470

// Complex aspect composition

471

suite("Complex Integration Suite")(

472

test("external service integration") { /* test */ },

473

test("database integration") { /* test */ },

474

test("file system integration") { /* test */ }

475

) @@ timeout(60.seconds) @@

476

parallel @@

477

eventually @@

478

tag("integration", "external") @@

479

ifEnv("RUN_INTEGRATION_TESTS", "true")

480

481

// Conditional aspects

482

val productionAspects =

483

when(sys.env.contains("PRODUCTION"))(timeout(10.seconds)) @@

484

when(sys.env.get("PARALLEL").contains("true"))(parallel) @@

485

tag("production")

486

487

suite("Production Tests")(

488

test("health check") { /* test */ }

489

) @@ productionAspects

490

```

491

492

## Custom Test Aspects

493

494

Creating custom aspects for domain-specific testing needs.

495

496

```scala { .api }

497

/**

498

* Create custom test aspect from transformation function

499

* @param transform function that transforms test specs

500

* @return custom test aspect

501

*/

502

def custom[R, E](

503

transform: Spec[R, E] => Spec[R, E]

504

): TestAspect[Nothing, R, Nothing, E] =

505

new TestAspect[Nothing, R, Nothing, E] {

506

def apply[R1 >: Nothing <: R, E1 >: Nothing <: E](

507

spec: Spec[R1, E1]

508

): Spec[R1, E1] = transform(spec)

509

}

510

511

/**

512

* Create aspect that modifies test environment

513

* @param layer environment transformation layer

514

* @return aspect that provides the layer to tests

515

*/

516

def provideLayer[R, R1, E](

517

layer: ZLayer[R, E, R1]

518

): TestAspect[Nothing, R, Nothing, Any] =

519

new TestAspect[Nothing, R, Nothing, Any] {

520

def apply[R2 >: Nothing <: R, E2 >: Nothing <: Any](

521

spec: Spec[R2, E2]

522

): Spec[R2, E2] = spec.provideLayer(layer)

523

}

524

```

525

526

**Usage Examples:**

527

528

```scala

529

import zio.test._

530

531

// Custom retry with logging

532

val retryWithLogging = TestAspect.custom[Any, Any] { spec =>

533

spec.mapTest { test =>

534

test.tapError(error =>

535

Console.printLine(s"Test failed, retrying: $error")

536

).retry(Schedule.recurs(2))

537

}

538

}

539

540

// Database transaction aspect

541

val inTransaction = TestAspect.provideLayer(

542

DatabaseTransactionLayer.test

543

)

544

545

// Usage

546

suite("Database Tests")(

547

test("user creation") { /* test */ },

548

test("user retrieval") { /* test */ }

549

) @@ inTransaction @@ retryWithLogging

550

```