or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arbitrary.mdcogen.mdgenerators.mdindex.mdproperties.mdproperty-collections.mdshrinking.mdstateful-testing.mdtest-execution.md

property-collections.mddocs/

0

# Property Collections

1

2

ScalaCheck's Properties framework enables organizing related properties into testable suites with command-line runners, batch execution support, and hierarchical organization. Property collections provide structure for large test suites and enable systematic testing of complex systems.

3

4

## Capabilities

5

6

### Core Properties Class

7

8

The fundamental class for organizing and executing collections of named properties.

9

10

```scala { .api }

11

open class Properties(val name: String) {

12

val name: String

13

def properties: Seq[(String, Prop)]

14

def check(prms: Test.Parameters = Test.Parameters.default): Unit

15

def main(args: Array[String]): Unit

16

def include(ps: Properties): Unit

17

def include(ps: Properties, prefix: String): Unit

18

def overrideParameters(p: Test.Parameters): Test.Parameters

19

}

20

```

21

22

**Usage Examples:**

23

```scala

24

object MathProperties extends Properties("Mathematics") {

25

property("addition commutative") = forAll { (a: Int, b: Int) =>

26

a + b == b + a

27

}

28

29

property("multiplication identity") = forAll { (a: Int) =>

30

a * 1 == a

31

}

32

33

property("division by self") = forAll { (a: Int) =>

34

(a != 0) ==> (a / a == 1)

35

}

36

}

37

38

// Check all properties

39

MathProperties.check()

40

41

// Run from command line

42

MathProperties.main(Array("--verbosity", "2"))

43

```

44

45

### Property Specification

46

47

The mechanism for adding properties to collections using assignment syntax.

48

49

```scala { .api }

50

sealed class PropertySpecifier {

51

def update(propName: String, p: => Prop): Unit

52

}

53

54

val property: PropertySpecifier

55

```

56

57

**Usage Examples:**

58

```scala

59

object StringProperties extends Properties("String Operations") {

60

// Using property specifier with assignment syntax

61

property("reverse twice") = forAll { (s: String) =>

62

s.reverse.reverse == s

63

}

64

65

property("concatenation length") = forAll { (s1: String, s2: String) =>

66

(s1 + s2).length == s1.length + s2.length

67

}

68

69

property("empty string identity") = forAll { (s: String) =>

70

s + "" == s && "" + s == s

71

}

72

}

73

```

74

75

### Property Specification with Seeds

76

77

Advanced property specification that allows explicit seed control for reproducible testing.

78

79

```scala { .api }

80

sealed class PropertyWithSeedSpecifier {

81

def update(propName: String, optSeed: Option[String], p: => Prop): Unit

82

}

83

84

val propertyWithSeed: PropertyWithSeedSpecifier

85

```

86

87

**Usage Examples:**

88

```scala

89

object ReproducibleTests extends Properties("Reproducible") {

90

// Property with explicit seed for reproducibility

91

propertyWithSeed("deterministic test", Some("SGVsbG8gV29ybGQ")) = forAll { (x: Int) =>

92

x + 0 == x

93

}

94

95

// Property without seed (will use random seed but show it on failure)

96

propertyWithSeed("debug test", None) = forAll { (data: ComplexData) =>

97

processData(data).isValid

98

}

99

}

100

```

101

102

### Property Collection Inclusion

103

104

Mechanism for composing larger test suites from smaller property collections.

105

106

```scala { .api }

107

def include(ps: Properties): Unit

108

def include(ps: Properties, prefix: String): Unit

109

```

110

111

**Usage Examples:**

112

```scala

113

object CoreLogic extends Properties("Core") {

114

property("basic invariant") = forAll { (x: Int) => x == x }

115

}

116

117

object AdvancedLogic extends Properties("Advanced") {

118

property("complex invariant") = forAll { (data: ComplexType) =>

119

validateComplex(data)

120

}

121

}

122

123

object AllTests extends Properties("Complete Test Suite") {

124

// Include other property collections

125

include(CoreLogic)

126

include(AdvancedLogic, "advanced.")

127

128

// Additional properties specific to this suite

129

property("integration test") = forAll { (a: Int, b: String) =>

130

integrate(a, b).nonEmpty

131

}

132

}

133

134

// This will run all properties from included collections plus local properties

135

AllTests.check()

136

```

137

138

### Parameter Customization

139

140

Override default test parameters for specific property collections.

141

142

```scala { .api }

143

def overrideParameters(p: Test.Parameters): Test.Parameters

144

```

145

146

**Usage Examples:**

147

```scala

148

object PerformanceTests extends Properties("Performance") {

149

// Custom parameters for this collection

150

override def overrideParameters(p: Test.Parameters): Test.Parameters = {

151

p.withMinSuccessfulTests(10000)

152

.withWorkers(4)

153

.withMaxSize(1000)

154

}

155

156

property("large data processing") = forAll { (data: List[Int]) =>

157

processLargeDataset(data).size <= data.size

158

}

159

}

160

161

object QuickTests extends Properties("Quick Smoke Tests") {

162

override def overrideParameters(p: Test.Parameters): Test.Parameters = {

163

p.withMinSuccessfulTests(10)

164

.withMaxSize(20)

165

}

166

167

property("smoke test") = forAll { (x: Int) => x + 1 > x }

168

}

169

```

170

171

### Command-Line Execution

172

173

Built-in command-line runner with parameter parsing and result reporting.

174

175

```scala { .api }

176

def main(args: Array[String]): Unit

177

```

178

179

**Usage Examples:**

180

```scala

181

object CommandLineTests extends Properties("CLI Tests") {

182

property("always passes") = Prop.passed

183

property("sometimes fails") = forAll { (x: Int) => x != 42 }

184

}

185

186

// Command line usage:

187

// scala CommandLineTests --minSuccessfulTests 1000 --workers 4

188

// scala CommandLineTests --verbosity 2 --maxDiscardRatio 10

189

190

// In application code:

191

CommandLineTests.main(Array(

192

"--minSuccessfulTests", "500",

193

"--workers", "2",

194

"--verbosity", "1"

195

))

196

```

197

198

## Property Organization Patterns

199

200

### Hierarchical Test Organization

201

202

```scala

203

// Domain-specific property collections

204

object UserManagement extends Properties("User Management") {

205

property("user creation") = forAll { (name: String, email: String) =>

206

val user = createUser(name, email)

207

user.name == name && user.email == email

208

}

209

210

property("user validation") = forAll { (user: User) =>

211

validateUser(user).isValid ==> user.isComplete

212

}

213

}

214

215

object OrderProcessing extends Properties("Order Processing") {

216

property("order total calculation") = forAll { (items: List[OrderItem]) =>

217

val order = Order(items)

218

order.total >= 0 && order.total == items.map(_.price).sum

219

}

220

}

221

222

object DatabaseOperations extends Properties("Database") {

223

property("CRUD operations") = forAll { (entity: Entity) =>

224

val saved = database.save(entity)

225

val retrieved = database.findById(saved.id)

226

retrieved.contains(saved)

227

}

228

}

229

230

// Top-level test suite

231

object ApplicationTestSuite extends Properties("E-Commerce Application") {

232

include(UserManagement, "users.")

233

include(OrderProcessing, "orders.")

234

include(DatabaseOperations, "db.")

235

236

property("end-to-end workflow") = forAll { (user: User, items: List[Item]) =>

237

val order = createOrder(user, items)

238

processPayment(order).isSuccess ==> order.status == OrderStatus.Paid

239

}

240

}

241

```

242

243

### Environment-Specific Testing

244

245

```scala

246

trait TestEnvironment {

247

def databaseUrl: String

248

def apiEndpoint: String

249

}

250

251

object LocalTestEnv extends TestEnvironment {

252

def databaseUrl = "jdbc:h2:mem:test"

253

def apiEndpoint = "http://localhost:8080"

254

}

255

256

object StagingTestEnv extends TestEnvironment {

257

def databaseUrl = "jdbc:postgresql://staging-db:5432/app"

258

def apiEndpoint = "https://staging-api.example.com"

259

}

260

261

abstract class EnvironmentProperties(name: String, env: TestEnvironment) extends Properties(name) {

262

override def overrideParameters(p: Test.Parameters): Test.Parameters = {

263

p.withTestCallback(ConsoleReporter(1))

264

.withMinSuccessfulTests(if (env == LocalTestEnv) 100 else 1000)

265

}

266

}

267

268

object LocalTests extends EnvironmentProperties("Local Tests", LocalTestEnv) {

269

property("database connection") = Prop {

270

connectToDatabase(LocalTestEnv.databaseUrl).isSuccess

271

}

272

}

273

274

object StagingTests extends EnvironmentProperties("Staging Tests", StagingTestEnv) {

275

property("api availability") = Prop {

276

httpGet(StagingTestEnv.apiEndpoint + "/health").status == 200

277

}

278

}

279

```

280

281

### Property Categorization

282

283

```scala

284

object PropertyCategories {

285

// Fast unit tests

286

object UnitTests extends Properties("Unit Tests") {

287

override def overrideParameters(p: Test.Parameters): Test.Parameters = {

288

p.withMinSuccessfulTests(100).withMaxSize(50)

289

}

290

291

property("string operations") = forAll { (s: String) =>

292

s.toLowerCase.toUpperCase.toLowerCase == s.toLowerCase

293

}

294

}

295

296

// Slower integration tests

297

object IntegrationTests extends Properties("Integration Tests") {

298

override def overrideParameters(p: Test.Parameters): Test.Parameters = {

299

p.withMinSuccessfulTests(50).withWorkers(2)

300

}

301

302

property("service integration") = forAll { (request: ServiceRequest) =>

303

val response = callExternalService(request)

304

response.isValid && response.correlationId == request.id

305

}

306

}

307

308

// Performance-focused tests

309

object PerformanceTests extends Properties("Performance Tests") {

310

override def overrideParameters(p: Test.Parameters): Test.Parameters = {

311

p.withMinSuccessfulTests(1000)

312

.withMaxSize(10000)

313

.withWorkers(Runtime.getRuntime.availableProcessors())

314

}

315

316

property("large data processing") = forAll { (data: List[DataRecord]) =>

317

val start = System.currentTimeMillis()

318

val result = processLargeDataset(data)

319

val duration = System.currentTimeMillis() - start

320

321

result.size == data.size && duration < 5000 // Under 5 seconds

322

}

323

}

324

}

325

326

// Master suite that includes all categories

327

object AllTests extends Properties("Complete Test Suite") {

328

include(PropertyCategories.UnitTests, "unit.")

329

include(PropertyCategories.IntegrationTests, "integration.")

330

include(PropertyCategories.PerformanceTests, "performance.")

331

}

332

```

333

334

### Test Result Analysis

335

336

```scala

337

object TestAnalyzer extends Properties("Analyzer") {

338

property("analyzed property") = forAll { (data: TestData) =>

339

classify(data.size == 0, "empty") {

340

classify(data.size < 10, "small", "large") {

341

collect(data.category) {

342

processTestData(data).isValid

343

}

344

}

345

}

346

}

347

}

348

349

// Custom test runner with result analysis

350

object CustomTestRunner {

351

def runWithAnalysis(props: Properties): Unit = {

352

val results = Test.checkProperties(

353

Test.Parameters.default.withTestCallback(ConsoleReporter(2)),

354

props

355

)

356

357

val (passed, failed) = results.partition(_._2.passed)

358

359

println(s"\n=== Test Results ===")

360

println(s"Passed: ${passed.size}")

361

println(s"Failed: ${failed.size}")

362

363

if (failed.nonEmpty) {

364

println(s"\nFailed Properties:")

365

failed.foreach { case (name, result) =>

366

println(s" ✗ $name")

367

result.status match {

368

case Test.Failed(args, labels) =>

369

println(s" Args: ${args.map(_.arg).mkString(", ")}")

370

if (labels.nonEmpty) println(s" Labels: ${labels.mkString(", ")}")

371

case Test.PropException(_, ex, _) =>

372

println(s" Exception: ${ex.getMessage}")

373

case _ =>

374

}

375

}

376

}

377

378

// Analyze collected data

379

results.foreach { case (name, result) =>

380

if (result.freqMap.total > 0) {

381

println(s"\n$name - Collected Data:")

382

result.freqMap.getRatios.take(5).foreach { case (item, ratio) =>

383

println(f" $item: ${ratio * 100}%.1f%%")

384

}

385

}

386

}

387

}

388

}

389

390

// Usage

391

CustomTestRunner.runWithAnalysis(ApplicationTestSuite)

392

```

393

394

### Conditional Property Execution

395

396

```scala

397

object ConditionalTests extends Properties("Conditional Tests") {

398

// Only run expensive tests if environment variable is set

399

if (sys.env.get("RUN_EXPENSIVE_TESTS").contains("true")) {

400

property("expensive computation") = forAll { (data: LargeDataSet) =>

401

expensiveComputation(data).isOptimal

402

}

403

}

404

405

// Platform-specific tests

406

if (System.getProperty("os.name").toLowerCase.contains("linux")) {

407

property("linux-specific feature") = forAll { (path: String) =>

408

linuxSpecificOperation(path).isSuccess

409

}

410

}

411

412

// Database-dependent tests

413

try {

414

connectToTestDatabase()

415

property("database operations") = forAll { (entity: Entity) =>

416

saveToDatabase(entity).isSuccess

417

}

418

} catch {

419

case _: Exception =>

420

println("Skipping database tests - database not available")

421

}

422

}

423

```

424

425

### Nested Property Collections

426

427

```scala

428

abstract class ModuleTests(moduleName: String) extends Properties(s"$moduleName Module") {

429

def unitTests: Properties

430

def integrationTests: Properties

431

432

include(unitTests, "unit.")

433

include(integrationTests, "integration.")

434

}

435

436

object AuthenticationModule extends ModuleTests("Authentication") {

437

object unitTests extends Properties("Auth Unit Tests") {

438

property("password hashing") = forAll { (password: String) =>

439

val hashed = hashPassword(password)

440

hashed != password && verifyPassword(password, hashed)

441

}

442

}

443

444

object integrationTests extends Properties("Auth Integration Tests") {

445

property("login flow") = forAll { (credentials: LoginCredentials) =>

446

val token = authenticate(credentials)

447

token.isValid ==> validateToken(token).isSuccess

448

}

449

}

450

}

451

452

object PaymentModule extends ModuleTests("Payment") {

453

object unitTests extends Properties("Payment Unit Tests") {

454

property("amount calculations") = forAll { (amount: BigDecimal, tax: Double) =>

455

val total = calculateTotal(amount, tax)

456

total >= amount && total > 0

457

}

458

}

459

460

object integrationTests extends Properties("Payment Integration Tests") {

461

property("payment processing") = forAll { (payment: PaymentRequest) =>

462

val result = processPayment(payment)

463

result.status == PaymentStatus.Success ==> result.transactionId.nonEmpty

464

}

465

}

466

}

467

468

object ApplicationModules extends Properties("All Modules") {

469

include(AuthenticationModule)

470

include(PaymentModule)

471

472

override def overrideParameters(p: Test.Parameters): Test.Parameters = {

473

p.withMinSuccessfulTests(200).withWorkers(3)

474

}

475

}

476

```