or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdconfiguration.mdexceptions.mdfixtures.mdindex.mdtest-suites.mdtransforms.md

fixtures.mddocs/

0

# Fixtures

1

2

Flexible fixture system for managing test resources with both synchronous and asynchronous support. Fixtures provide setup and teardown functionality for tests that need external resources like databases, files, or network connections.

3

4

## Capabilities

5

6

### AnyFixture - Base Fixture Class

7

8

Base class for all fixture types, providing the core lifecycle methods for resource management.

9

10

```scala { .api }

11

/**

12

* Base class for all fixtures that manage test resources

13

* @param fixtureName Name identifier for the fixture

14

*/

15

abstract class AnyFixture[T](val fixtureName: String) {

16

17

/** Get the fixture value (the managed resource) */

18

def apply(): T

19

20

/** Setup performed once before all tests in the suite */

21

def beforeAll(): Any = ()

22

23

/** Setup performed before each individual test */

24

def beforeEach(context: BeforeEach): Any = ()

25

26

/** Cleanup performed after each individual test */

27

def afterEach(context: AfterEach): Any = ()

28

29

/** Cleanup performed once after all tests in the suite */

30

def afterAll(): Any = ()

31

}

32

```

33

34

### Fixture - Synchronous Fixture

35

36

Fixture for synchronous resource management where setup and teardown operations complete immediately.

37

38

```scala { .api }

39

/**

40

* Synchronous fixture for resources that don't require async operations

41

* @param name The fixture name

42

*/

43

abstract class Fixture[T](name: String) extends AnyFixture[T](name) {

44

45

/** Synchronous setup before all tests */

46

override def beforeAll(): Unit = ()

47

48

/** Synchronous setup before each test */

49

override def beforeEach(context: BeforeEach): Unit = ()

50

51

/** Synchronous cleanup after each test */

52

override def afterEach(context: AfterEach): Unit = ()

53

54

/** Synchronous cleanup after all tests */

55

override def afterAll(): Unit = ()

56

}

57

```

58

59

**Usage Examples:**

60

61

```scala

62

class DatabaseFixture extends Fixture[Database]("database") {

63

private var db: Database = _

64

65

def apply(): Database = db

66

67

override def beforeAll(): Unit = {

68

db = Database.createInMemory()

69

db.migrate()

70

}

71

72

override def afterAll(): Unit = {

73

db.close()

74

}

75

76

override def beforeEach(context: BeforeEach): Unit = {

77

db.clearAll()

78

db.seedTestData()

79

}

80

}

81

82

class DatabaseTests extends FunSuite {

83

val database = new DatabaseFixture()

84

override def munitFixtures = List(database)

85

86

test("user creation") {

87

val db = database()

88

val user = db.createUser("Alice", "alice@example.com")

89

assertEquals(user.name, "Alice")

90

}

91

92

test("user lookup") {

93

val db = database()

94

db.createUser("Bob", "bob@example.com")

95

val found = db.findUserByEmail("bob@example.com")

96

assert(found.isDefined)

97

}

98

}

99

```

100

101

### FutureFixture - Asynchronous Fixture

102

103

Fixture for asynchronous resource management where setup and teardown operations return `Future[Unit]`.

104

105

```scala { .api }

106

/**

107

* Asynchronous fixture for resources requiring async setup/teardown

108

* @param name The fixture name

109

*/

110

abstract class FutureFixture[T](name: String) extends AnyFixture[T](name) {

111

112

/** Asynchronous setup before all tests */

113

override def beforeAll(): Future[Unit] = Future.successful(())

114

115

/** Asynchronous setup before each test */

116

override def beforeEach(context: BeforeEach): Future[Unit] = Future.successful(())

117

118

/** Asynchronous cleanup after each test */

119

override def afterEach(context: AfterEach): Future[Unit] = Future.successful(())

120

121

/** Asynchronous cleanup after all tests */

122

override def afterAll(): Future[Unit] = Future.successful(())

123

}

124

```

125

126

**Usage Examples:**

127

128

```scala

129

import scala.concurrent.Future

130

import scala.concurrent.ExecutionContext.Implicits.global

131

132

class HttpServerFixture extends FutureFixture[HttpServer]("httpServer") {

133

private var server: HttpServer = _

134

135

def apply(): HttpServer = server

136

137

override def beforeAll(): Future[Unit] = {

138

server = new HttpServer()

139

server.start().map(_ => ())

140

}

141

142

override def afterAll(): Future[Unit] = {

143

server.stop()

144

}

145

146

override def beforeEach(context: BeforeEach): Future[Unit] = {

147

server.clearRoutes().map(_ => ())

148

}

149

}

150

151

class HttpServerTests extends FunSuite {

152

val httpServer = new HttpServerFixture()

153

override def munitFixtures = List(httpServer)

154

155

test("server responds to GET") {

156

val server = httpServer()

157

for {

158

_ <- server.addRoute("GET", "/hello", "Hello, World!")

159

response <- server.get("/hello")

160

} yield {

161

assertEquals(response.body, "Hello, World!")

162

}

163

}

164

}

165

```

166

167

### FunFixtures - Function-Style Fixtures

168

169

Function-style fixtures that provide a more flexible approach to resource management with per-test setup and teardown.

170

171

```scala { .api }

172

/**

173

* Trait providing function-style fixture support (mixed into BaseFunSuite)

174

*/

175

trait FunFixtures {

176

177

/**

178

* Function-style fixture that provides setup/teardown per test

179

*/

180

class FunFixture[T] private (

181

setup: TestOptions => Future[T],

182

teardown: T => Future[Unit]

183

) {

184

185

/** Define a test that uses this fixture */

186

def test(name: String)(body: T => Any)(implicit loc: Location): Unit

187

188

/** Define a test with options that uses this fixture */

189

def test(options: TestOptions)(body: T => Any)(implicit loc: Location): Unit

190

}

191

}

192

193

/**

194

* Factory methods for creating FunFixtures

195

*/

196

object FunFixture {

197

198

/** Create a synchronous fixture */

199

def apply[T](

200

setup: TestOptions => T,

201

teardown: T => Unit

202

): FunFixture[T]

203

204

/** Create an asynchronous fixture */

205

def async[T](

206

setup: TestOptions => Future[T],

207

teardown: T => Future[Unit]

208

): FunFixture[T]

209

210

/** Combine two fixtures into a tuple */

211

def map2[A, B](

212

a: FunFixture[A],

213

b: FunFixture[B]

214

): FunFixture[(A, B)]

215

216

/** Combine three fixtures into a tuple */

217

def map3[A, B, C](

218

a: FunFixture[A],

219

b: FunFixture[B],

220

c: FunFixture[C]

221

): FunFixture[(A, B, C)]

222

}

223

```

224

225

**Usage Examples:**

226

227

```scala

228

class FunFixtureExamples extends FunSuite {

229

230

// Simple synchronous fixture

231

val tempDir = FunFixture[Path](

232

setup = { _ => Files.createTempDirectory("test") },

233

teardown = { dir => Files.deleteRecursively(dir) }

234

)

235

236

tempDir.test("file operations") { dir =>

237

val file = dir.resolve("test.txt")

238

Files.write(file, "Hello, World!")

239

val content = Files.readString(file)

240

assertEquals(content, "Hello, World!")

241

}

242

243

// Asynchronous fixture

244

val httpClient = FunFixture.async[HttpClient](

245

setup = { _ => HttpClient.create() },

246

teardown = { client => client.close() }

247

)

248

249

httpClient.test("HTTP request") { client =>

250

client.get("https://api.example.com/status").map { response =>

251

assertEquals(response.status, 200)

252

}

253

}

254

255

// Fixture with test-specific setup

256

val database = FunFixture.async[Database](

257

setup = { testOptions =>

258

val dbName = s"test_${testOptions.name.replaceAll("\\s+", "_")}"

259

Database.create(dbName).map { db =>

260

db.migrate()

261

db

262

}

263

},

264

teardown = { db => db.drop() }

265

)

266

267

database.test("user operations") { db =>

268

for {

269

user <- db.createUser("Alice")

270

found <- db.findUser(user.id)

271

} yield {

272

assertEquals(found.map(_.name), Some("Alice"))

273

}

274

}

275

276

// Combined fixtures

277

val dbAndClient = FunFixture.map2(database, httpClient)

278

279

dbAndClient.test("integration test") { case (db, client) =>

280

for {

281

user <- db.createUser("Bob")

282

response <- client.post("/api/users", user.toJson)

283

} yield {

284

assertEquals(response.status, 201)

285

}

286

}

287

}

288

```

289

290

### Lifecycle Context

291

292

Context objects passed to fixture lifecycle methods containing information about the current test.

293

294

```scala { .api }

295

/**

296

* Context passed to beforeEach methods

297

* @param test The test that is about to run

298

*/

299

class BeforeEach(val test: Test) extends Serializable

300

301

/**

302

* Context passed to afterEach methods

303

* @param test The test that just completed

304

*/

305

class AfterEach(val test: Test) extends Serializable

306

```

307

308

**Usage Examples:**

309

310

```scala

311

class LoggingFixture extends Fixture[Logger]("logger") {

312

private val logger = Logger.getLogger("test")

313

314

def apply(): Logger = logger

315

316

override def beforeEach(context: BeforeEach): Unit = {

317

logger.info(s"Starting test: ${context.test.name}")

318

}

319

320

override def afterEach(context: AfterEach): Unit = {

321

logger.info(s"Completed test: ${context.afterEach.test.name}")

322

}

323

}

324

```

325

326

## Fixture Patterns

327

328

### Resource Pools

329

330

```scala

331

class ConnectionPoolFixture extends FutureFixture[ConnectionPool]("connectionPool") {

332

private var pool: ConnectionPool = _

333

334

def apply(): ConnectionPool = pool

335

336

override def beforeAll(): Future[Unit] = {

337

pool = ConnectionPool.create(maxConnections = 10)

338

pool.initialize()

339

}

340

341

override def afterAll(): Future[Unit] = {

342

pool.shutdown()

343

}

344

}

345

```

346

347

### External Services

348

349

```scala

350

class MockServerFixture extends Fixture[MockServer]("mockServer") {

351

private var server: MockServer = _

352

353

def apply(): MockServer = server

354

355

override def beforeAll(): Unit = {

356

server = MockServer.start(8080)

357

}

358

359

override def afterAll(): Unit = {

360

server.stop()

361

}

362

363

override def beforeEach(context: BeforeEach): Unit = {

364

server.reset() // Clear all mocked endpoints

365

}

366

}

367

```

368

369

### Test Data

370

371

```scala

372

val testData = FunFixture[TestData](

373

setup = { testOptions =>

374

val data = if (testOptions.tags.contains(new Tag("large-dataset"))) {

375

TestData.generateLarge()

376

} else {

377

TestData.generateSmall()

378

}

379

data

380

},

381

teardown = { data => data.cleanup() }

382

)

383

```