or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions-matchers.mdasync-testing.mdindex.mdscalactic-utilities.mdtest-execution.mdtest-styles.md

async-testing.mddocs/

0

# Async Testing

1

2

ScalaTest provides comprehensive support for asynchronous testing with Future-based tests, automatic timeout handling, and integration with concurrent utilities. All test styles have async variants that return `Future[Assertion]` instead of `Assertion`.

3

4

## Capabilities

5

6

### Async Test Suites

7

8

All ScalaTest styles have async variants for testing asynchronous code.

9

10

```scala { .api }

11

import org.scalatest.funsuite.AsyncFunSuite

12

import org.scalatest.flatspec.AsyncFlatSpec

13

import org.scalatest.wordspec.AsyncWordSpec

14

import org.scalatest.freespec.AsyncFreeSpec

15

import org.scalatest.featurespec.AsyncFeatureSpec

16

import org.scalatest.funspec.AsyncFunSpec

17

import org.scalatest.propspec.AsyncPropSpec

18

19

// Base async test suite trait

20

trait AsyncTestSuite extends Suite with RecoverMethods with CompleteLastly {

21

// Test methods return Future[Assertion] instead of Assertion

22

protected def transformToOutcome(testFun: => Future[compatible.Assertion]): () => AsyncOutcome

23

24

// Execution context for running async tests

25

implicit def executionContext: ExecutionContext

26

27

// Timeout configuration

28

implicit val patienceConfig: PatienceConfig

29

}

30

31

// Async FunSuite example

32

abstract class AsyncFunSuite extends AsyncTestSuite with AsyncTestSuiteMixin {

33

protected final class AsyncFunSuiteAsyncTest(testName: String, testTags: Tag*)

34

extends AsyncTest(testName, testTags: _*)

35

36

def test(testName: String, testTags: Tag*)(testFun: => Future[compatible.Assertion]): Unit

37

def ignore(testName: String, testTags: Tag*)(testFun: => Future[compatible.Assertion]): Unit

38

}

39

```

40

41

**Async Test Examples:**

42

```scala

43

import org.scalatest.funsuite.AsyncFunSuite

44

import scala.concurrent.Future

45

import scala.concurrent.duration._

46

47

class AsyncCalculatorSuite extends AsyncFunSuite {

48

test("async addition should work") {

49

val futureResult = Future {

50

Thread.sleep(100) // Simulate async work

51

2 + 2

52

}

53

54

futureResult.map { result =>

55

assert(result === 4)

56

}

57

}

58

59

test("multiple async operations") {

60

val future1 = Future(10)

61

val future2 = Future(20)

62

63

for {

64

a <- future1

65

b <- future2

66

} yield {

67

assert(a + b === 30)

68

}

69

}

70

71

test("async failure handling") {

72

val failingFuture = Future.failed[Int](new RuntimeException("Expected failure"))

73

74

recoverToSucceededIf[RuntimeException] {

75

failingFuture

76

}

77

}

78

}

79

```

80

81

### Future Assertions

82

83

Specialized assertion methods for Future-based testing.

84

85

```scala { .api }

86

import org.scalatest.concurrent.ScalaFutures

87

88

trait ScalaFutures {

89

// Configuration for Future handling

90

implicit val patienceConfig: PatienceConfig

91

92

// Block until Future completes and apply function to result

93

def whenReady[T](future: Future[T], timeout: Timeout = timeout, interval: Interval = interval)

94

(fun: T => Unit): Unit

95

96

// Extract Future value (blocking)

97

implicit class FutureValues[T](future: Future[T]) {

98

def futureValue: T

99

def futureValue(timeout: Timeout): T

100

}

101

102

// Assert Future completes successfully

103

def noException should be thrownBy future

104

105

// Assert Future fails with specific exception

106

a [ExceptionType] should be thrownBy future

107

}

108

109

// Patience configuration

110

case class PatienceConfig(timeout: Span, interval: Span)

111

object PatienceConfig {

112

implicit val defaultPatienceConfig: PatienceConfig =

113

PatienceConfig(timeout = scaled(Span(150, Millis)), interval = scaled(Span(15, Millis)))

114

}

115

```

116

117

**Future Assertion Examples:**

118

```scala

119

import org.scalatest.flatspec.AnyFlatSpec

120

import org.scalatest.concurrent.ScalaFutures

121

import org.scalatest.matchers.should.Matchers

122

import scala.concurrent.Future

123

import scala.concurrent.duration._

124

125

class FutureAssertionSpec extends AnyFlatSpec with Matchers with ScalaFutures {

126

implicit override val patienceConfig = PatienceConfig(

127

timeout = scaled(Span(5, Seconds)),

128

interval = scaled(Span(50, Millis))

129

)

130

131

"Future assertions" should "work with whenReady" in {

132

val future = Future { 42 }

133

134

whenReady(future) { result =>

135

result should be(42)

136

}

137

}

138

139

"Future values" should "be extractable" in {

140

val future = Future { "hello world" }

141

142

future.futureValue should startWith("hello")

143

future.futureValue should have length 11

144

}

145

146

"Failed futures" should "be testable" in {

147

val failedFuture = Future.failed[String](new IllegalArgumentException("Bad argument"))

148

149

a [IllegalArgumentException] should be thrownBy {

150

failedFuture.futureValue

151

}

152

}

153

}

154

```

155

156

### Async Recovery Methods

157

158

Handle and test for expected failures in async code.

159

160

```scala { .api }

161

trait RecoverMethods {

162

// Recover from expected exception type

163

def recoverToSucceededIf[T <: AnyRef](future: Future[Any])

164

(implicit classTag: ClassTag[T]): Future[Assertion]

165

166

// Recover and return the exception

167

def recoverToExceptionIf[T <: AnyRef](future: Future[Any])

168

(implicit classTag: ClassTag[T]): Future[T]

169

}

170

171

// Usage in async tests

172

class AsyncRecoverySpec extends AsyncFunSuite {

173

test("should recover from expected exception") {

174

val failingFuture = Future.failed[Int](new IllegalArgumentException("Expected"))

175

176

recoverToSucceededIf[IllegalArgumentException] {

177

failingFuture

178

}

179

}

180

181

test("should capture and examine exception") {

182

val failingFuture = Future.failed[Int](new IllegalArgumentException("Test message"))

183

184

recoverToExceptionIf[IllegalArgumentException] {

185

failingFuture

186

}.map { exception =>

187

assert(exception.getMessage === "Test message")

188

}

189

}

190

}

191

```

192

193

### Async Fixture Support

194

195

Manage asynchronous test fixtures and resources.

196

197

```scala { .api }

198

import org.scalatest.funsuite.FixtureAsyncFunSuite

199

200

// Async fixture suite

201

abstract class FixtureAsyncFunSuite extends FixtureAsyncTestSuite with AsyncTestSuiteMixin {

202

type FixtureParam

203

204

// Async fixture management

205

def withFixture(test: OneArgAsyncTest): FutureOutcome

206

207

// Async test definition

208

def test(testName: String, testTags: Tag*)(testFun: FixtureParam => Future[compatible.Assertion]): Unit

209

}

210

211

// Async fixture example

212

class AsyncDatabaseSuite extends FixtureAsyncFunSuite {

213

type FixtureParam = Database

214

215

override def withFixture(test: OneArgAsyncTest): FutureOutcome = {

216

val database = Database.connect()

217

complete {

218

super.withFixture(test.toNoArgAsyncTest(database))

219

} lastly {

220

database.close()

221

}

222

}

223

224

test("async database operations") { db =>

225

for {

226

_ <- db.insert("user", Map("name" -> "Alice"))

227

user <- db.findByName("Alice")

228

} yield {

229

assert(user.name === "Alice")

230

}

231

}

232

}

233

```

234

235

### Eventually and Async Patience

236

237

Test conditions that should eventually become true.

238

239

```scala { .api }

240

import org.scalatest.concurrent.Eventually

241

242

trait Eventually {

243

// Repeatedly test condition until it succeeds or times out

244

def eventually[T](fun: => T)(implicit config: PatienceConfig): T

245

246

// Configuration for eventually

247

implicit val patienceConfig: PatienceConfig

248

}

249

250

// Integration with async tests

251

trait AsyncEventually extends Eventually {

252

def eventually[T](fun: => Future[T])(implicit config: PatienceConfig): Future[T]

253

}

254

```

255

256

**Eventually Examples:**

257

```scala

258

import org.scalatest.concurrent.Eventually

259

import org.scalatest.time.{Millis, Seconds, Span}

260

261

class EventuallySpec extends AnyFlatSpec with Eventually {

262

implicit override val patienceConfig = PatienceConfig(

263

timeout = scaled(Span(5, Seconds)),

264

interval = scaled(Span(100, Millis))

265

)

266

267

"Eventually" should "wait for condition to become true" in {

268

var counter = 0

269

270

eventually {

271

counter += 1

272

assert(counter >= 10)

273

}

274

}

275

276

"Eventually with async" should "work with futures" in {

277

@volatile var ready = false

278

279

// Simulate async operation that sets ready flag

280

Future {

281

Thread.sleep(1000)

282

ready = true

283

}

284

285

eventually {

286

assert(ready === true)

287

}

288

}

289

}

290

```

291

292

### Async Test Execution Context

293

294

Configure execution context for async tests.

295

296

```scala { .api }

297

// Default execution context

298

import org.scalatest.concurrent.ScalaFutures._

299

300

trait AsyncTestSuite {

301

// Default execution context (can be overridden)

302

implicit def executionContext: ExecutionContext =

303

scala.concurrent.ExecutionContext.Implicits.global

304

}

305

306

// Custom execution context

307

class CustomAsyncSuite extends AsyncFunSuite {

308

// Use custom thread pool

309

implicit override def executionContext: ExecutionContext =

310

ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))

311

}

312

```

313

314

### Async Timeouts and Configuration

315

316

Configure timeouts and patience parameters for async operations.

317

318

```scala { .api }

319

import org.scalatest.time._

320

321

// Time span specifications

322

case class Span(length: Long, unit: Units)

323

324

// Timeout and interval configuration

325

case class PatienceConfig(timeout: Span, interval: Span) {

326

def scaled(factor: Double): PatienceConfig

327

}

328

329

// Predefined time units

330

object Span {

331

def apply(length: Long, unit: Units): Span

332

}

333

334

sealed abstract class Units

335

case object Nanosecond extends Units

336

case object Nanoseconds extends Units

337

case object Microsecond extends Units

338

case object Microseconds extends Units

339

case object Millisecond extends Units

340

case object Milliseconds extends Units

341

case object Millis extends Units

342

case object Second extends Units

343

case object Seconds extends Units

344

case object Minute extends Units

345

case object Minutes extends Units

346

case object Hour extends Units

347

case object Hours extends Units

348

case object Day extends Units

349

case object Days extends Units

350

```

351

352

**Timeout Configuration Examples:**

353

```scala

354

import org.scalatest.time._

355

import scala.concurrent.duration._

356

357

class TimeoutConfigSpec extends AsyncFunSuite {

358

// Custom patience configuration

359

implicit override val patienceConfig = PatienceConfig(

360

timeout = scaled(Span(10, Seconds)),

361

interval = scaled(Span(200, Millis))

362

)

363

364

test("long running operation") {

365

val longFuture = Future {

366

Thread.sleep(2000) // 2 second delay

367

"completed"

368

}

369

370

longFuture.map { result =>

371

assert(result === "completed")

372

}

373

}

374

375

test("operation with custom timeout") {

376

val future = Future { "result" }

377

378

whenReady(future, timeout(5.seconds)) { result =>

379

assert(result === "result")

380

}

381

}

382

}

383

```

384

385

### Async Parallel Testing

386

387

Run async tests in parallel for better performance.

388

389

```scala { .api }

390

// Parallel async execution

391

trait ParallelTestExecution { this: AsyncTestSuite =>

392

// Enable parallel execution of async tests

393

}

394

395

class ParallelAsyncSuite extends AsyncFunSuite with ParallelTestExecution {

396

test("parallel async test 1") {

397

Future {

398

Thread.sleep(100)

399

assert(1 + 1 === 2)

400

}

401

}

402

403

test("parallel async test 2") {

404

Future {

405

Thread.sleep(100)

406

assert(2 + 2 === 4)

407

}

408

}

409

}

410

```

411

412

### Integration with Reactive Streams

413

414

Test reactive streams and async data processing.

415

416

```scala { .api }

417

// Example with Akka Streams (not part of ScalaTest core)

418

import akka.stream.scaladsl.Source

419

import akka.stream.testkit.scaladsl.TestSink

420

421

class StreamTestSpec extends AsyncFunSuite {

422

test("stream processing") {

423

val source = Source(1 to 10)

424

.map(_ * 2)

425

.filter(_ > 10)

426

427

val future = source.runFold(0)(_ + _)

428

429

future.map { sum =>

430

assert(sum === 60) // 12 + 14 + 16 + 18 + 20 = 80

431

}

432

}

433

}

434

```

435

436

### Async Error Handling Patterns

437

438

Common patterns for handling errors in async tests.

439

440

```scala

441

class AsyncErrorHandlingSpec extends AsyncFunSuite {

442

test("transform failures") {

443

val future = Future.failed[Int](new RuntimeException("Original error"))

444

445

future.recover {

446

case _: RuntimeException => 42

447

}.map { result =>

448

assert(result === 42)

449

}

450

}

451

452

test("chain operations with error handling") {

453

def mightFail(value: Int): Future[Int] = {

454

if (value < 0) Future.failed(new IllegalArgumentException("Negative value"))

455

else Future.successful(value * 2)

456

}

457

458

val result = for {

459

a <- Future.successful(5)

460

b <- mightFail(a)

461

c <- mightFail(b)

462

} yield c

463

464

result.map { finalValue =>

465

assert(finalValue === 20) // 5 * 2 * 2

466

}

467

}

468

469

test("verify specific failure") {

470

val future = Future.failed[String](new IllegalStateException("Bad state"))

471

472

recoverToSucceededIf[IllegalStateException] {

473

future

474

}

475

}

476

}