or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdindex.mdmocking.mdproperty-testing.mdtest-aspects.mdtest-environment.mdtest-execution.mdtest-specifications.md

property-testing.mddocs/

0

# Property Testing

1

2

Comprehensive property-based testing with generators, automatic shrinking, and configurable test execution.

3

4

## Capabilities

5

6

### Property Test Execution Functions

7

8

Functions for running property-based tests with different execution strategies.

9

10

```scala { .api }

11

/**

12

* Tests a property with random samples from generator

13

* @param rv - Generator for test values

14

* @param test - Property test function

15

* @returns Test result aggregating all sample results

16

*/

17

def check[R <: TestConfig, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult]

18

19

/**

20

* Tests an effectful property with random samples

21

* @param rv - Generator for test values

22

* @param test - Effectful property test function

23

* @returns ZIO effect producing aggregated test result

24

*/

25

def checkM[R <: TestConfig, R1 <: R, E, A](rv: Gen[R, A])(test: A => ZIO[R1, E, TestResult]): ZIO[R1, E, TestResult]

26

27

/**

28

* Tests a property exhaustively with all samples from generator

29

* @param rv - Generator for test values (should be finite)

30

* @param test - Property test function

31

* @returns Test result for all generated values

32

*/

33

def checkAll[R <: TestConfig, A](rv: Gen[R, A])(test: A => TestResult): URIO[R, TestResult]

34

35

/**

36

* Tests an effectful property exhaustively

37

* @param rv - Generator for test values (should be finite)

38

* @param test - Effectful property test function

39

* @returns ZIO effect producing complete test result

40

*/

41

def checkAllM[R <: TestConfig, R1 <: R, E, A](rv: Gen[R, A])(test: A => ZIO[R1, E, TestResult]): ZIO[R1, E, TestResult]

42

43

/**

44

* Tests a property with specific number of samples

45

* @param n - Number of samples to generate

46

* @returns CheckN instance for chaining with generator and test

47

*/

48

def checkN(n: Int): CheckVariants.CheckN

49

50

/**

51

* Tests an effectful property with specific number of samples

52

* @param n - Number of samples to generate

53

* @returns CheckNM instance for chaining with generator and test

54

*/

55

def checkNM(n: Int): CheckVariants.CheckNM

56

```

57

58

### Gen Class and Core Operations

59

60

The main generator class with monadic operations and transformations.

61

62

```scala { .api }

63

/**

64

* A generator that produces values of type A in environment R

65

* @tparam R - Environment requirements (usually TestConfig or Sized)

66

* @tparam A - Type of generated values

67

*/

68

abstract class Gen[-R, +A] {

69

/** Transform generated values with a function */

70

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

71

72

/** Monadic composition of generators */

73

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

74

75

/** Filter generated values based on predicate */

76

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

77

78

/** Enable for-comprehension syntax */

79

def withFilter(f: A => Boolean): Gen[R, A]

80

81

/** Combine with another generator producing tuples */

82

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

83

84

/** Combine generators with transformation function */

85

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

86

87

/** Get stream of samples for testing */

88

def sample: ZStream[R, Nothing, Sample[R, A]]

89

90

/** Disable shrinking for this generator */

91

def noShrink: Gen[R, A]

92

93

/** Make generator produce optional values */

94

def optional: Gen[R, Option[A]]

95

96

/** Set specific size for generation */

97

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

98

99

/** Set size using an effect */

100

def resizeM[R1 <: R](size: ZIO[R1, Nothing, Int]): Gen[R1, A]

101

}

102

```

103

104

### Basic Value Generators

105

106

Fundamental generators for creating constant and computed values.

107

108

```scala { .api }

109

/** Generator that always produces unit value */

110

val unit: Gen[Any, Unit]

111

112

/** Generator that produces constant value */

113

def const[A](a: => A): Gen[Any, A]

114

115

/** Generator that produces pure value */

116

def succeed[A](a: A): Gen[Any, A]

117

118

/** Generator that produces lazy value */

119

def succeedLazy[A](a: => A): Gen[Any, A]

120

121

/** Generator that produces failure */

122

def fail[E](error: E): Gen[Any, E]

123

124

/** Generator that throws exception */

125

def failingWith[A](gen: Gen[Any, Throwable]): Gen[Any, A]

126

```

127

128

### Primitive Type Generators

129

130

Built-in generators for primitive Scala types.

131

132

```scala { .api }

133

/** Boolean generator */

134

val boolean: Gen[Any, Boolean]

135

136

/** Byte generator with size-based range */

137

val byte: Gen[Sized, Byte]

138

139

/** Short generator with size-based range */

140

val short: Gen[Sized, Short]

141

142

/** Int generator with size-based range */

143

val int: Gen[Sized, Int]

144

145

/** Long generator with size-based range */

146

val long: Gen[Sized, Long]

147

148

/** Float generator */

149

val float: Gen[Sized, Float]

150

151

/** Double generator */

152

val double: Gen[Sized, Double]

153

154

/** BigInt generator */

155

val bigInt: Gen[Sized, BigInt]

156

157

/** BigDecimal generator */

158

val bigDecimal: Gen[Sized, BigDecimal]

159

160

/** Character generator */

161

val char: Gen[Any, Char]

162

163

/** String generator with size-based length */

164

val string: Gen[Sized, String]

165

```

166

167

### Range-based Generators

168

169

Generators that produce values within specified ranges.

170

171

```scala { .api }

172

/** Int generator within range */

173

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

174

175

/** Long generator within range */

176

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

177

178

/** Double generator within range */

179

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

180

181

/** Float generator within range */

182

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

183

184

/** BigInt generator within range */

185

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

186

187

/** BigDecimal generator within range */

188

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

189

190

/** Character generator within range */

191

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

192

```

193

194

### Character Generators

195

196

Specialized generators for different character sets.

197

198

```scala { .api }

199

/** Alphabetic characters (a-z, A-Z) */

200

val alphaChar: Gen[Any, Char]

201

202

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

203

val alphaNumericChar: Gen[Any, Char]

204

205

/** Lowercase alphabetic characters (a-z) */

206

val alphaLowerChar: Gen[Any, Char]

207

208

/** Uppercase alphabetic characters (A-Z) */

209

val alphaUpperChar: Gen[Any, Char]

210

211

/** ASCII characters */

212

val asciiChar: Gen[Any, Char]

213

214

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

215

val hexChar: Gen[Any, Char]

216

217

/** Numeric characters (0-9) */

218

val numericChar: Gen[Any, Char]

219

220

/** Printable characters */

221

val printableChar: Gen[Any, Char]

222

```

223

224

### String Generators

225

226

Generators for strings with various constraints.

227

228

```scala { .api }

229

/** Non-empty string generator using character generator */

230

def string1(charGen: Gen[Any, Char]): Gen[Sized, String]

231

232

/** String generator using specific character generator */

233

def stringGen(charGen: Gen[Any, Char]): Gen[Sized, String]

234

235

/** String of fixed length */

236

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

237

238

/** String within length bounds */

239

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

240

```

241

242

### Collection Generators

243

244

Generators that produce collections of values.

245

246

```scala { .api }

247

/** List generator with size-based length */

248

def listOf[R, A](gen: Gen[R, A]): Gen[R with Sized, List[A]]

249

250

/** Non-empty list generator */

251

def listOf1[R, A](gen: Gen[R, A]): Gen[R with Sized, ::[A]]

252

253

/** List generator with fixed size */

254

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

255

256

/** List generator with bounded size */

257

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

258

259

/** Vector generator with size-based length */

260

def vectorOf[R, A](gen: Gen[R, A]): Gen[R with Sized, Vector[A]]

261

262

/** Non-empty vector generator */

263

def vectorOf1[R, A](gen: Gen[R, A]): Gen[R with Sized, Vector[A]]

264

265

/** Vector generator with fixed size */

266

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

267

268

/** Set generator with size-based cardinality */

269

def setOf[R, A](gen: Gen[R, A]): Gen[R with Sized, Set[A]]

270

271

/** Non-empty set generator */

272

def setOf1[R, A](gen: Gen[R, A]): Gen[R with Sized, Set[A]]

273

274

/** Set generator with fixed size */

275

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

276

277

/** Map generator with size-based cardinality */

278

def mapOf[R, A, B](keyGen: Gen[R, A], valueGen: Gen[R, B]): Gen[R with Sized, Map[A, B]]

279

280

/** Non-empty map generator */

281

def mapOf1[R, A, B](keyGen: Gen[R, A], valueGen: Gen[R, B]): Gen[R with Sized, Map[A, B]]

282

283

/** Map generator with fixed size */

284

def mapOfN[R, A, B](n: Int)(keyGen: Gen[R, A], valueGen: Gen[R, B]): Gen[R, Map[A, B]]

285

```

286

287

### Choice and Option Generators

288

289

Generators for selecting from alternatives and creating optional values.

290

291

```scala { .api }

292

/** Generator that chooses from multiple generators */

293

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

294

295

/** Generator that chooses from values */

296

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

297

298

/** Generator that chooses from collection */

299

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

300

301

/** Weighted choice generator */

302

def weighted[R, A](gs: (Gen[R, A], Double)*): Gen[R, A]

303

304

/** None generator */

305

val none: Gen[Any, Option[Nothing]]

306

307

/** Some generator from another generator */

308

def some[R, A](gen: Gen[R, A]): Gen[R, Option[A]]

309

310

/** Optional generator (Some or None) */

311

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

312

```

313

314

### Either Generators

315

316

Generators for Either values.

317

318

```scala { .api }

319

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

320

def either[R, A, B](left: Gen[R, A], right: Gen[R, B]): Gen[R, Either[A, B]]

321

322

/** Left generator */

323

def left[R, A](gen: Gen[R, A]): Gen[R, Either[A, Nothing]]

324

325

/** Right generator */

326

def right[R, A](gen: Gen[R, A]): Gen[R, Either[Nothing, A]]

327

```

328

329

### Sample Class

330

331

Represents generated samples with shrinking capabilities.

332

333

```scala { .api }

334

/**

335

* A generated sample that can be shrunk for failure minimization

336

* @tparam R - Environment requirements

337

* @tparam A - Type of sample value

338

*/

339

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

340

/** Transform sample value */

341

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

342

343

/** Monadic composition */

344

def flatMap[R1 <: R, B](f: A => Sample[R1, B]): Sample[R1, B]

345

346

/** Filter sample */

347

def filter(f: A => Boolean): Sample[R, A]

348

349

/** Transform with effect */

350

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

351

352

/** Search for minimal failing sample */

353

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

354

}

355

```

356

357

## Usage Examples

358

359

```scala

360

import zio.test._

361

import zio.test.Gen._

362

363

// Basic property test

364

test("string length property") {

365

check(string) { s =>

366

assert(s.reverse.reverse)(equalTo(s))

367

}

368

}

369

370

// Multiple generators

371

test("addition property") {

372

check(int, int) { (a, b) =>

373

assert(a + b)(equalTo(b + a))

374

}

375

}

376

377

// Custom generator

378

val evenInt = int.filter(_ % 2 == 0)

379

380

test("even numbers") {

381

check(evenInt) { n =>

382

assert(n % 2)(equalTo(0))

383

}

384

}

385

386

// Collection properties

387

test("list concatenation") {

388

check(listOf(int), listOf(int)) { (xs, ys) =>

389

assert((xs ++ ys).length)(equalTo(xs.length + ys.length))

390

}

391

}

392

393

// Effectful property test

394

testM("async property") {

395

checkM(int) { n =>

396

for {

397

result <- someAsyncOperation(n)

398

} yield assert(result)(isGreaterThan(n))

399

}

400

}

401

402

// Exhaustive testing with finite generator

403

test("small numbers exhaustive") {

404

checkAll(int(1, 10)) { n =>

405

assert(n * n)(isGreaterThan(0))

406

}

407

}

408

409

// Fixed number of samples

410

test("large sample test") {

411

checkN(1000)(double) { d =>

412

assert(d + 0.0)(equalTo(d))

413

}

414

}

415

```