or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdcore-testing.mdindex.mdproperty-testing.mdsmart-assertions.mdtest-aspects.mdtest-services.md

property-testing.mddocs/

0

# Property-Based Testing

1

2

Generator system for creating random test data and property-based testing with automatic shrinking support. Property-based testing validates code against general properties rather than specific examples.

3

4

## Capabilities

5

6

### Core Generator Type

7

8

The fundamental generator type for producing test data.

9

10

```scala { .api }

11

/**

12

* A generator of values of type A requiring environment R

13

* @tparam R - Environment type required for generation

14

* @tparam A - Type of values to generate

15

*/

16

case class Gen[-R, +A](sample: ZStream[R, Nothing, Sample[R, A]]) {

17

/** Transform generated values */

18

def map[B](f: A => B)(implicit trace: Trace): Gen[R, B]

19

20

/** Effectfully transform generated values */

21

def mapZIO[R1 <: R, B](f: A => ZIO[R1, Nothing, B])(implicit trace: Trace): Gen[R1, B]

22

23

/** Monadic composition of generators */

24

def flatMap[R1 <: R, B](f: A => Gen[R1, B])(implicit trace: Trace): Gen[R1, B]

25

26

/** Filter generated values by predicate */

27

def filter(f: A => Boolean)(implicit trace: Trace): Gen[R, A]

28

29

/** Effectfully filter generated values */

30

def filterZIO[R1 <: R](f: A => ZIO[R1, Nothing, Boolean])(implicit trace: Trace): Gen[R1, A]

31

32

/** Collect values using partial function */

33

def collect[B](pf: PartialFunction[A, B])(implicit trace: Trace): Gen[R, B]

34

35

/** Combine two generators */

36

def zip[R1 <: R, B](that: Gen[R1, B])(implicit trace: Trace): Gen[R1, (A, B)]

37

38

/** Combine generators with custom function */

39

def zipWith[R1 <: R, B, C](that: Gen[R1, B])(f: (A, B) => C)(implicit trace: Trace): Gen[R1, C]

40

41

/** Concatenate with another generator */

42

def concat[R1 <: R, A1 >: A](that: Gen[R1, A1])(implicit trace: Trace): Gen[R1, A1]

43

44

/** Disable shrinking for this generator */

45

def noShrink(implicit trace: Trace): Gen[R, A]

46

47

/** Replace shrinking strategy */

48

def reshrink[R1 <: R](f: A => ZStream[R1, Nothing, A])(implicit trace: Trace): Gen[R1, A]

49

50

/** Set size parameter for generator */

51

def resize(size: Int)(implicit trace: Trace): Gen[R, A]

52

}

53

```

54

55

### Sample Type

56

57

Container for generated values with shrinking information.

58

59

```scala { .api }

60

/**

61

* A sample contains a generated value and shrinking information

62

* @tparam R - Environment type

63

* @tparam A - Value type

64

*/

65

case class Sample[-R, +A](value: A, shrink: ZStream[R, Nothing, Sample[R, A]]) {

66

/** Transform the sample value */

67

def map[B](f: A => B): Sample[R, B]

68

69

/** Apply function to each element in shrink tree */

70

def foreach[R1 <: R, B](f: A => ZIO[R1, Nothing, B])(implicit trace: Trace): ZIO[R1, Nothing, Sample[R1, B]]

71

72

/** Search through shrinks for values matching predicate */

73

def shrinkSearch(f: A => Boolean)(implicit trace: Trace): ZStream[R, Nothing, A]

74

}

75

```

76

77

### Property Testing Functions

78

79

Core functions for running property-based tests.

80

81

```scala { .api }

82

/**

83

* Check property with sufficient number of samples

84

* @param rv - Generator producing test values

85

* @param test - Property test function

86

* @return ZIO effect producing TestResult

87

*/

88

def check[R, A, In](rv: Gen[R, A])(test: A => In)(implicit

89

checkConstructor: CheckConstructor[R, In],

90

sourceLocation: SourceLocation,

91

trace: Trace

92

): ZIO[checkConstructor.OutEnvironment, checkConstructor.OutError, TestResult]

93

94

/**

95

* Check property for all values from finite generator

96

* @param rv - Finite generator producing test values

97

* @param test - Property test function

98

* @return ZIO effect producing TestResult

99

*/

100

def checkAll[R, A, In](rv: Gen[R, A])(test: A => In)(implicit

101

checkConstructor: CheckConstructor[R, In],

102

sourceLocation: SourceLocation,

103

trace: Trace

104

): ZIO[checkConstructor.OutEnvironment, checkConstructor.OutError, TestResult]

105

106

/**

107

* Check property with specific number of samples

108

* @param n - Number of samples to test

109

* @return CheckN instance for applying to generators

110

*/

111

def checkN(n: Int): CheckVariants.CheckN

112

113

/**

114

* Check property in parallel with sufficient samples

115

* @param rv - Generator producing test values

116

* @param parallelism - Number of parallel workers

117

* @param test - Property test function

118

* @return ZIO effect producing TestResult

119

*/

120

def checkPar[R, A, In](rv: Gen[R, A], parallelism: Int)(test: A => In)(implicit

121

checkConstructor: CheckConstructor[R, In],

122

sourceLocation: SourceLocation,

123

trace: Trace

124

): ZIO[checkConstructor.OutEnvironment, checkConstructor.OutError, TestResult]

125

126

/**

127

* Check all values from finite generator in parallel

128

* @param rv - Finite generator producing test values

129

* @param parallelism - Number of parallel workers

130

* @param test - Property test function

131

* @return ZIO effect producing TestResult

132

*/

133

def checkAllPar[R, A, In](rv: Gen[R, A], parallelism: Int)(test: A => In)(implicit

134

checkConstructor: CheckConstructor[R, In],

135

sourceLocation: SourceLocation,

136

trace: Trace

137

): ZIO[checkConstructor.OutEnvironment, checkConstructor.OutError, TestResult]

138

```

139

140

**Usage Examples:**

141

142

```scala

143

import zio.test._

144

import zio.test.Gen._

145

146

test("property-based testing") {

147

// Simple property test

148

check(int) { n =>

149

assertTrue((n + 1) > n)

150

} &&

151

152

// Test with multiple generators

153

check(int, string) { (n, s) =>

154

assertTrue(s.length >= 0 && n.toString.nonEmpty)

155

} &&

156

157

// Test with specific number of samples

158

checkN(1000)(double) { d =>

159

assertTrue(!d.isNaN || d.isNaN) // Always true

160

} &&

161

162

// Test all values from finite generator

163

checkAll(oneOf(1, 2, 3, 4, 5)) { n =>

164

assertTrue(n >= 1 && n <= 5)

165

}

166

}

167

```

168

169

### Primitive Generators

170

171

Generators for basic Scala types.

172

173

```scala { .api }

174

/** Boolean generator */

175

def boolean: Gen[Any, Boolean]

176

177

/** Byte generator with full range */

178

def byte: Gen[Any, Byte]

179

180

/** Byte generator within specified bounds */

181

def byteWith(min: Byte, max: Byte): Gen[Any, Byte]

182

183

/** Short generator with full range */

184

def short: Gen[Any, Short]

185

186

/** Short generator within specified bounds */

187

def shortWith(min: Short, max: Short): Gen[Any, Short]

188

189

/** Int generator with full range */

190

def int: Gen[Any, Int]

191

192

/** Int generator within specified bounds */

193

def intWith(min: Int, max: Int): Gen[Any, Int]

194

195

/** Long generator with full range */

196

def long: Gen[Any, Long]

197

198

/** Long generator within specified bounds */

199

def longWith(min: Long, max: Long): Gen[Any, Long]

200

201

/** Float generator */

202

def float: Gen[Any, Float]

203

204

/** Float generator within specified bounds */

205

def floatWith(min: Float, max: Float): Gen[Any, Float]

206

207

/** Double generator */

208

def double: Gen[Any, Double]

209

210

/** Double generator within specified bounds */

211

def doubleWith(min: Double, max: Double): Gen[Any, Double]

212

213

/** BigInt generator */

214

def bigInt: Gen[Any, BigInt]

215

216

/** BigInt generator within specified bounds */

217

def bigIntWith(min: BigInt, max: BigInt): Gen[Any, BigInt]

218

219

/** BigDecimal generator */

220

def bigDecimal: Gen[Any, BigDecimal]

221

222

/** BigDecimal generator within specified bounds */

223

def bigDecimalWith(min: BigDecimal, max: BigDecimal): Gen[Any, BigDecimal]

224

225

/** Java BigInteger generator */

226

def bigIntegerJava: Gen[Any, java.math.BigInteger]

227

228

/** Java BigDecimal generator */

229

def bigDecimalJava: Gen[Any, java.math.BigDecimal]

230

```

231

232

**Usage Examples:**

233

234

```scala

235

import zio.test._

236

import zio.test.Gen._

237

238

test("primitive generators") {

239

check(boolean) { b =>

240

assertTrue(b == true || b == false)

241

} &&

242

243

check(intWith(1, 100)) { n =>

244

assertTrue(n >= 1 && n <= 100)

245

} &&

246

247

check(doubleWith(0.0, 1.0)) { d =>

248

assertTrue(d >= 0.0 && d <= 1.0)

249

}

250

}

251

```

252

253

### Character and String Generators

254

255

Generators for characters and strings with various constraints.

256

257

```scala { .api }

258

/** Character generator with full Unicode range */

259

def char: Gen[Any, Char]

260

261

/** Character generator within specified bounds */

262

def charWith(min: Char, max: Char): Gen[Any, Char]

263

264

/** Alphabetic character generator (a-z, A-Z) */

265

def alphaChar: Gen[Any, Char]

266

267

/** Alphanumeric character generator (a-z, A-Z, 0-9) */

268

def alphaNumericChar: Gen[Any, Char]

269

270

/** Numeric character generator (0-9) */

271

def numericChar: Gen[Any, Char]

272

273

/** ASCII character generator (0-127) */

274

def asciiChar: Gen[Any, Char]

275

276

/** Printable ASCII character generator */

277

def printableChar: Gen[Any, Char]

278

279

/** Hexadecimal character generator (0-9, a-f, A-F) */

280

def hexChar: Gen[Any, Char]

281

282

/** Lowercase hexadecimal character generator (0-9, a-f) */

283

def hexCharLower: Gen[Any, Char]

284

285

/** Uppercase hexadecimal character generator (0-9, A-F) */

286

def hexCharUpper: Gen[Any, Char]

287

288

/** Unicode character generator */

289

def unicodeChar: Gen[Any, Char]

290

291

/** Whitespace character generator */

292

def whitespaceChars: Gen[Any, Char]

293

294

/** String generator using printable characters */

295

def string: Gen[Any, String]

296

297

/** Non-empty string generator */

298

def string1: Gen[Any, String]

299

300

/** Fixed-length string generator */

301

def stringN(n: Int): Gen[Any, String]

302

303

/** Bounded-length string generator */

304

def stringBounded(min: Int, max: Int): Gen[Any, String]

305

306

/** Alphanumeric string generator */

307

def alphaNumericString: Gen[Any, String]

308

309

/** Bounded alphanumeric string generator */

310

def alphaNumericStringBounded(min: Int, max: Int): Gen[Any, String]

311

312

/** ASCII string generator */

313

def asciiString: Gen[Any, String]

314

315

/** ISO-8859-1 string generator */

316

def iso_8859_1: Gen[Any, String]

317

```

318

319

**Usage Examples:**

320

321

```scala

322

import zio.test._

323

import zio.test.Gen._

324

325

test("character and string generators") {

326

check(alphaChar) { c =>

327

assertTrue(c.isLetter)

328

} &&

329

330

check(alphaNumericString) { s =>

331

assertTrue(s.forall(c => c.isLetterOrDigit))

332

} &&

333

334

check(stringBounded(5, 10)) { s =>

335

assertTrue(s.length >= 5 && s.length <= 10)

336

}

337

}

338

```

339

340

### Collection Generators

341

342

Generators for various collection types.

343

344

```scala { .api }

345

/** List generator using another generator */

346

def listOf[A](gen: Gen[Any, A]): Gen[Any, List[A]]

347

348

/** Non-empty list generator */

349

def listOf1[A](gen: Gen[Any, A]): Gen[Any, List[A]]

350

351

/** Fixed-size list generator */

352

def listOfN[A](n: Int)(gen: Gen[Any, A]): Gen[Any, List[A]]

353

354

/** Bounded-size list generator */

355

def listOfBounded[A](min: Int, max: Int)(gen: Gen[Any, A]): Gen[Any, List[A]]

356

357

/** Vector generator using another generator */

358

def vectorOf[A](gen: Gen[Any, A]): Gen[Any, Vector[A]]

359

360

/** Non-empty vector generator */

361

def vectorOf1[A](gen: Gen[Any, A]): Gen[Any, Vector[A]]

362

363

/** Fixed-size vector generator */

364

def vectorOfN[A](n: Int)(gen: Gen[Any, A]): Gen[Any, Vector[A]]

365

366

/** Bounded-size vector generator */

367

def vectorOfBounded[A](min: Int, max: Int)(gen: Gen[Any, A]): Gen[Any, Vector[A]]

368

369

/** Chunk generator using another generator */

370

def chunkOf[A](gen: Gen[Any, A]): Gen[Any, Chunk[A]]

371

372

/** Non-empty chunk generator */

373

def chunkOf1[A](gen: Gen[Any, A]): Gen[Any, Chunk[A]]

374

375

/** Fixed-size chunk generator */

376

def chunkOfN[A](n: Int)(gen: Gen[Any, A]): Gen[Any, Chunk[A]]

377

378

/** Bounded-size chunk generator */

379

def chunkOfBounded[A](min: Int, max: Int)(gen: Gen[Any, A]): Gen[Any, Chunk[A]]

380

381

/** Set generator using another generator */

382

def setOf[A](gen: Gen[Any, A]): Gen[Any, Set[A]]

383

384

/** Non-empty set generator */

385

def setOf1[A](gen: Gen[Any, A]): Gen[Any, Set[A]]

386

387

/** Fixed-size set generator */

388

def setOfN[A](n: Int)(gen: Gen[Any, A]): Gen[Any, Set[A]]

389

390

/** Bounded-size set generator */

391

def setOfBounded[A](min: Int, max: Int)(gen: Gen[Any, A]): Gen[Any, Set[A]]

392

393

/** Map generator using key and value generators */

394

def mapOf[K, V](keyGen: Gen[Any, K], valueGen: Gen[Any, V]): Gen[Any, Map[K, V]]

395

396

/** Non-empty map generator */

397

def mapOf1[K, V](keyGen: Gen[Any, K], valueGen: Gen[Any, V]): Gen[Any, Map[K, V]]

398

399

/** Fixed-size map generator */

400

def mapOfN[K, V](n: Int)(keyGen: Gen[Any, K], valueGen: Gen[Any, V]): Gen[Any, Map[K, V]]

401

402

/** Bounded-size map generator */

403

def mapOfBounded[K, V](min: Int, max: Int)(keyGen: Gen[Any, K], valueGen: Gen[Any, V]): Gen[Any, Map[K, V]]

404

```

405

406

**Usage Examples:**

407

408

```scala

409

import zio.test._

410

import zio.test.Gen._

411

412

test("collection generators") {

413

check(listOf(int)) { numbers =>

414

assertTrue(numbers.forall(_.isInstanceOf[Int]))

415

} &&

416

417

check(listOfBounded(1, 5)(string)) { strings =>

418

assertTrue(strings.size >= 1 && strings.size <= 5)

419

} &&

420

421

check(setOf(intWith(1, 10))) { numbers =>

422

assertTrue(numbers.forall(n => n >= 1 && n <= 10))

423

} &&

424

425

check(mapOf(string, int)) { map =>

426

assertTrue(map.keys.forall(_.isInstanceOf[String]) &&

427

map.values.forall(_.isInstanceOf[Int]))

428

}

429

}

430

```

431

432

### Option and Either Generators

433

434

Generators for Option and Either types.

435

436

```scala { .api }

437

/** Option generator that may produce Some or None */

438

def option[A](gen: Gen[Any, A]): Gen[Any, Option[A]]

439

440

/** Some generator that always produces Some */

441

def some[A](gen: Gen[Any, A]): Gen[Any, Some[A]]

442

443

/** None generator that always produces None */

444

def none: Gen[Any, None.type]

445

446

/** Either generator using left and right generators */

447

def either[A, B](leftGen: Gen[Any, A], rightGen: Gen[Any, B]): Gen[Any, Either[A, B]]

448

```

449

450

### Special Utility Generators

451

452

Generators for specific use cases and combinations.

453

454

```scala { .api }

455

/** Constant value generator */

456

def const[A](value: A): Gen[Any, A]

457

458

/** Constant sample generator with shrinking */

459

def constSample[R, A](sample: Sample[R, A]): Gen[R, A]

460

461

/** Generator from specific elements */

462

def elements[A](as: A*): Gen[Any, A]

463

464

/** Generator choosing from multiple generators */

465

def oneOf[A](gens: Gen[Any, A]*): Gen[Any, A]

466

467

/** Weighted generator selection */

468

def weighted[A](weightedGens: (Gen[Any, A], Double)*): Gen[Any, A]

469

470

/** Generator from iterable collection */

471

def fromIterable[A](as: Iterable[A]): Gen[Any, A]

472

473

/** Generator from ZIO effect */

474

def fromZIO[R, A](zio: ZIO[R, Nothing, A]): Gen[R, A]

475

476

/** Generator from ZIO sample effect */

477

def fromZIOSample[R, A](zio: ZIO[R, Nothing, Sample[R, A]]): Gen[R, A]

478

479

/** Lazy generator evaluation */

480

def suspend[R, A](gen: => Gen[R, A]): Gen[R, A]

481

482

/** Empty generator (produces no values) */

483

def empty[A]: Gen[Any, A]

484

485

/** Unit generator */

486

def unit: Gen[Any, Unit]

487

488

/** UUID generator */

489

def uuid: Gen[Any, java.util.UUID]

490

491

/** Currency generator */

492

def currency: Gen[Any, java.util.Currency]

493

494

/** Throwable generator */

495

def throwable: Gen[Any, Throwable]

496

```

497

498

**Usage Examples:**

499

500

```scala

501

import zio.test._

502

import zio.test.Gen._

503

504

test("special generators") {

505

check(const(42)) { n =>

506

assertTrue(n == 42)

507

} &&

508

509

check(elements("red", "green", "blue")) { color =>

510

assertTrue(Set("red", "green", "blue").contains(color))

511

} &&

512

513

check(oneOf(int, string.map(_.length))) { value =>

514

assertTrue(value.isInstanceOf[Int])

515

} &&

516

517

check(option(string)) { maybeString =>

518

assertTrue(maybeString.isEmpty || maybeString.get.isInstanceOf[String])

519

}

520

}

521

```

522

523

### Size-Related Generators

524

525

Generators that work with size parameters.

526

527

```scala { .api }

528

/** Current size generator */

529

def size: Gen[Sized, Int]

530

531

/** Size-dependent generator */

532

def sized[R, A](f: Int => Gen[R, A]): Gen[R with Sized, A]

533

534

/** Small-sized generator (size / 4) */

535

def small[R, A](gen: Gen[R, A]): Gen[R, A]

536

537

/** Medium-sized generator (size / 2) */

538

def medium[R, A](gen: Gen[R, A]): Gen[R, A]

539

540

/** Large-sized generator (size * 2) */

541

def large[R, A](gen: Gen[R, A]): Gen[R, A]

542

```

543

544

### Mathematical Distribution Generators

545

546

Generators for specific mathematical distributions.

547

548

```scala { .api }

549

/** Uniform distribution generator */

550

def uniform[A: Numeric](min: A, max: A): Gen[Any, A]

551

552

/** Exponential distribution generator */

553

def exponential[A: Numeric](lambda: A): Gen[Any, A]

554

```

555

556

### Combinator Functions

557

558

Functions for combining and transforming generators.

559

560

```scala { .api }

561

/** Collect all generator values into a collection */

562

def collectAll[R, A](gens: Iterable[Gen[R, A]]): Gen[R, List[A]]

563

564

/** Concatenate multiple generators */

565

def concatAll[R, A](gens: Iterable[Gen[R, A]]): Gen[R, A]

566

```

567

568

**Usage Examples:**

569

570

```scala

571

import zio.test._

572

import zio.test.Gen._

573

574

test("size and distribution generators") {

575

check(sized(n => listOfN(n)(int))) { numbers =>

576

assertTrue(numbers.size >= 0)

577

} &&

578

579

check(uniform(1.0, 10.0)) { d =>

580

assertTrue(d >= 1.0 && d <= 10.0)

581

} &&

582

583

check(small(listOf(int))) { numbers =>

584

assertTrue(numbers.size <= 25) // Typically smaller due to size reduction

585

}

586

}

587

```