or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async.mdfixtures.mdindex.mdmatchers.mdproperty.mdscalactic.mdtest-styles.md

async.mddocs/

0

# Asynchronous Testing

1

2

ScalaTest provides comprehensive support for testing asynchronous code with Futures, custom execution contexts, and async test suites. This enables testing of non-blocking operations while maintaining proper test isolation and timing control.

3

4

## Capabilities

5

6

### AsyncTestSuite

7

8

Base trait for test suites that work with asynchronous operations returning Futures.

9

10

```scala { .api }

11

/**

12

* Base trait for asynchronous test suites

13

*/

14

trait AsyncTestSuite extends Suite with RecoverMethods with CompleteLastly {

15

16

/**

17

* Implicit execution context for running async operations

18

*/

19

implicit def executionContext: ExecutionContext

20

21

/**

22

* Transform test function to handle async operations

23

*/

24

def transformToFuture(testFun: => Future[compatible.Assertion]): FutureOutcome

25

26

/**

27

* Run a single asynchronous test

28

*/

29

def runAsyncTest(

30

testName: String,

31

args: Args,

32

includeIcon: Boolean,

33

invokeWithAsyncTestDataFunction: AsyncTestDataInvoker

34

): FutureOutcome

35

}

36

37

/**

38

* Async version of FunSuite

39

*/

40

abstract class AsyncFunSuite extends AsyncTestSuite with AsyncTestRegistration {

41

/**

42

* Register an asynchronous test

43

*/

44

protected def test(testName: String)(testFun: => Future[compatible.Assertion]): Unit

45

}

46

47

/**

48

* Async version of FlatSpec

49

*/

50

abstract class AsyncFlatSpec extends AsyncTestSuite with AsyncTestRegistration {

51

protected final class AsyncFlatSpecStringWrapper(string: String) {

52

def should(testFun: => Future[compatible.Assertion]): Unit

53

def must(testFun: => Future[compatible.Assertion]): Unit

54

def can(testFun: => Future[compatible.Assertion]): Unit

55

}

56

57

protected implicit def convertToAsyncFlatSpecStringWrapper(o: String): AsyncFlatSpecStringWrapper

58

}

59

60

/**

61

* Async versions of other test styles

62

*/

63

abstract class AsyncFunSpec extends AsyncTestSuite with AsyncTestRegistration

64

abstract class AsyncWordSpec extends AsyncTestSuite with AsyncTestRegistration

65

abstract class AsyncFreeSpec extends AsyncTestSuite with AsyncTestRegistration

66

abstract class AsyncFeatureSpec extends AsyncTestSuite with AsyncTestRegistration

67

```

68

69

**Usage Examples:**

70

71

```scala

72

import org.scalatest.funsuite.AsyncFunSuite

73

import scala.concurrent.Future

74

75

class AsyncExampleSuite extends AsyncFunSuite {

76

77

test("async computation should complete successfully") {

78

val futureResult = Future {

79

Thread.sleep(100) // Simulate async work

80

42

81

}

82

83

futureResult.map { result =>

84

assert(result == 42)

85

}

86

}

87

88

test("async operation with assertions") {

89

def asyncOperation(): Future[String] = Future.successful("Hello, World!")

90

91

asyncOperation().map { result =>

92

result should include ("World")

93

result should have length 13

94

}

95

}

96

}

97

```

98

99

### FutureOutcome

100

101

Wrapper for Future[Outcome] that provides transformation and composition methods.

102

103

```scala { .api }

104

/**

105

* Wrapper for Future[Outcome] with convenient transformation methods

106

*/

107

class FutureOutcome(private[scalatest] val underlying: Future[Outcome]) {

108

109

/**

110

* Transform the outcome using a function

111

*/

112

def map(f: Outcome => Outcome)(implicit executionContext: ExecutionContext): FutureOutcome

113

114

/**

115

* FlatMap operation for chaining FutureOutcomes

116

*/

117

def flatMap(f: Outcome => FutureOutcome)(implicit executionContext: ExecutionContext): FutureOutcome

118

119

/**

120

* Transform the Future[Outcome] directly

121

*/

122

def transform(f: Try[Outcome] => Try[Outcome])(implicit executionContext: ExecutionContext): FutureOutcome

123

124

/**

125

* Handle failures and recover with a different outcome

126

*/

127

def recover(pf: PartialFunction[Throwable, Outcome])(implicit executionContext: ExecutionContext): FutureOutcome

128

129

/**

130

* Convert to a Future[Outcome]

131

*/

132

def toFuture: Future[Outcome]

133

134

/**

135

* Block and wait for completion (mainly for testing)

136

*/

137

def isCompleted: Boolean

138

}

139

140

object FutureOutcome {

141

/**

142

* Create from a Future[Outcome]

143

*/

144

def apply(future: Future[Outcome]): FutureOutcome

145

146

/**

147

* Create from a successful outcome

148

*/

149

def successful(outcome: Outcome): FutureOutcome

150

151

/**

152

* Create from a failed outcome

153

*/

154

def failed(exception: Throwable): FutureOutcome

155

156

/**

157

* Create from a canceled outcome

158

*/

159

def canceled(exception: Throwable): FutureOutcome

160

}

161

```

162

163

**Usage Examples:**

164

165

```scala

166

import org.scalatest._

167

import scala.concurrent.Future

168

import scala.util.{Success, Failure}

169

170

// Creating FutureOutcome instances

171

val successfulOutcome = FutureOutcome.successful(Succeeded)

172

val failedOutcome = FutureOutcome.failed(new RuntimeException("Test failed"))

173

174

// Transforming outcomes

175

val transformedOutcome = successfulOutcome.map {

176

case Succeeded => Succeeded

177

case failed => failed

178

}

179

180

// Chaining operations

181

val chainedOutcome = successfulOutcome.flatMap { outcome =>

182

if (outcome == Succeeded) {

183

FutureOutcome.successful(Succeeded)

184

} else {

185

FutureOutcome.failed(new RuntimeException("Chain failed"))

186

}

187

}

188

189

// Error recovery

190

val recoveredOutcome = failedOutcome.recover {

191

case ex: RuntimeException => Failed(ex)

192

case other => Failed(other)

193

}

194

```

195

196

### Recovery Methods

197

198

Utilities for handling exceptions in asynchronous tests.

199

200

```scala { .api }

201

trait RecoverMethods {

202

203

/**

204

* Recover from specific exception types in async operations

205

*/

206

def recoverToSucceededIf[T <: AnyRef](future: Future[Any])(implicit classTag: ClassTag[T]): Future[Assertion]

207

208

/**

209

* Recover expecting a specific exception to be thrown

210

*/

211

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

212

}

213

```

214

215

**Usage Examples:**

216

217

```scala

218

import org.scalatest.RecoverMethods

219

import scala.concurrent.Future

220

221

class AsyncRecoveryExample extends AsyncFunSuite with RecoverMethods {

222

223

test("should recover from expected exception") {

224

val failingFuture = Future {

225

throw new IllegalArgumentException("Expected error")

226

}

227

228

// Test succeeds if the expected exception is thrown

229

recoverToSucceededIf[IllegalArgumentException] {

230

failingFuture

231

}

232

}

233

234

test("should capture expected exception") {

235

val failingFuture = Future {

236

throw new RuntimeException("Test error")

237

}

238

239

// Capture the exception for further assertions

240

recoverToExceptionIf[RuntimeException] {

241

failingFuture

242

}.map { exception =>

243

exception.getMessage should include ("Test error")

244

}

245

}

246

}

247

```

248

249

### Complete Lastly

250

251

Trait for cleanup operations that run after async tests complete.

252

253

```scala { .api }

254

trait CompleteLastly {

255

256

/**

257

* Register cleanup code to run after test completion

258

*/

259

def completeLastly(completeLastlyCode: => Unit): Unit

260

261

/**

262

* Register async cleanup code to run after test completion

263

*/

264

def completeLastly(completeLastlyCode: => Future[Unit]): Unit

265

}

266

```

267

268

**Usage Examples:**

269

270

```scala

271

class AsyncResourceExample extends AsyncFunSuite with CompleteLastly {

272

273

test("should cleanup resources after async test") {

274

val resource = acquireResource()

275

276

// Register cleanup that runs regardless of test outcome

277

completeLastly {

278

resource.close()

279

println("Resource cleaned up")

280

}

281

282

// Test code that uses the resource

283

Future {

284

resource.process()

285

assert(resource.isProcessed)

286

}

287

}

288

289

test("should handle async cleanup") {

290

val asyncResource = acquireAsyncResource()

291

292

// Register async cleanup

293

completeLastly {

294

asyncResource.cleanupAsync()

295

}

296

297

asyncResource.processAsync().map { result =>

298

assert(result.isSuccess)

299

}

300

}

301

}

302

```

303

304

### Async Fixtures

305

306

Support for asynchronous setup and teardown in test fixtures.

307

308

```scala { .api }

309

/**

310

* Async version of fixture test suites

311

*/

312

trait AsyncFixtureTestSuite extends AsyncTestSuite {

313

type FixtureParam

314

315

/**

316

* Async fixture method that provides test data

317

*/

318

def withAsyncFixture(test: OneArgAsyncTest): FutureOutcome

319

}

320

321

/**

322

* One argument async test function

323

*/

324

abstract class OneArgAsyncTest extends (FixtureParam => Future[Outcome]) with TestData {

325

def apply(fixture: FixtureParam): Future[Outcome]

326

}

327

```

328

329

**Usage Examples:**

330

331

```scala

332

import scala.concurrent.Future

333

334

class AsyncFixtureExample extends AsyncFunSuite {

335

336

case class DatabaseConnection(url: String) {

337

def query(sql: String): Future[List[String]] = Future.successful(List("result1", "result2"))

338

def close(): Future[Unit] = Future.successful(())

339

}

340

341

type FixtureParam = DatabaseConnection

342

343

def withAsyncFixture(test: OneArgAsyncTest): FutureOutcome = {

344

val connection = DatabaseConnection("jdbc:test://localhost")

345

346

complete {

347

withFixture(test.toNoArgAsyncTest(connection))

348

} lastly {

349

// Async cleanup

350

connection.close()

351

}

352

}

353

354

test("should query database asynchronously") { connection =>

355

connection.query("SELECT * FROM users").map { results =>

356

results should have size 2

357

results should contain ("result1")

358

}

359

}

360

}

361

```

362

363

## Types

364

365

```scala { .api }

366

/**

367

* Test outcome for async operations

368

*/

369

sealed abstract class Outcome extends Product with Serializable

370

case object Succeeded extends Outcome

371

final case class Failed(exception: Throwable) extends Outcome

372

final case class Canceled(exception: Throwable) extends Outcome

373

case object Pending extends Outcome

374

375

/**

376

* Async test registration

377

*/

378

trait AsyncTestRegistration {

379

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

380

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

381

}

382

383

/**

384

* Test data for async tests

385

*/

386

trait AsyncTestDataInvoker {

387

def apply(testData: TestData): Future[Outcome]

388

}

389

```