or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

http-client.mdindex.mdning.mdoauth.mdopenid.mdssl.mdtesting.md

testing.mddocs/

0

# Test Support

1

2

Testing utilities for HTTP client operations in Play applications. Play WS provides comprehensive testing support for both unit tests and integration tests with HTTP clients.

3

4

## Capabilities

5

6

### WS Test Client

7

8

Main testing interface for HTTP client operations.

9

10

```scala { .api }

11

/**

12

* Test client trait providing HTTP testing utilities

13

*/

14

trait WsTestClient {

15

/**

16

* Create a WSRequest for a Play Call (route)

17

* @param call Play framework Call (route definition)

18

* @param port Implicit test server port

19

* @param client Implicit WSClient instance

20

* @return WSRequest configured for the test server

21

*/

22

def wsCall(call: Call)(implicit port: Port, client: WSClient): WSRequest

23

24

/**

25

* Create a WSRequest for a URL relative to test server

26

* @param url URL path (will be prefixed with test server base URL)

27

* @param port Implicit test server port

28

* @param client Implicit WSClient instance

29

* @return WSRequest configured for the test server

30

*/

31

def wsUrl(url: String)(implicit port: Port, client: WSClient): WSRequest

32

33

/**

34

* Execute test block with a temporary WSClient

35

* @param block Test code block that uses WSClient

36

* @param port Implicit test server port

37

* @return Result of executing the test block

38

*/

39

def withClient[T](block: WSClient => T)(implicit port: Port): T

40

}

41

42

/**

43

* WS test client object implementing the trait

44

*/

45

object WsTestClient extends WsTestClient

46

```

47

48

### Basic Test Examples

49

50

**Testing with Play Test Server:**

51

52

```scala

53

import play.api.test._

54

import play.api.test.Helpers._

55

import play.api.libs.ws._

56

import org.scalatestplus.play._

57

58

class MyControllerSpec extends PlaySpec with OneServerPerSuite {

59

60

"MyController" should {

61

"return JSON data" in {

62

implicit val client = app.injector.instanceOf[WSClient]

63

64

val response = await(WsTestClient.wsUrl("/api/users").get())

65

66

response.status mustBe OK

67

response.header("Content-Type") must contain("application/json")

68

69

val json = response.json

70

(json \ "users").as[Seq[JsObject]] must not be empty

71

}

72

}

73

}

74

```

75

76

**Testing with Temporary Client:**

77

78

```scala

79

import play.api.test._

80

import play.api.test.Helpers._

81

82

class HttpClientSpec extends PlaySpec with OneServerPerSuite {

83

84

"HTTP client" should {

85

"handle external API calls" in {

86

WsTestClient.withClient { client =>

87

val response = await(client.url("https://api.example.com/test").get())

88

response.status mustBe OK

89

}

90

}

91

}

92

}

93

```

94

95

### Route-Based Testing

96

97

Test your application routes using the `wsCall` method.

98

99

```scala

100

import play.api.routing.sird._

101

import play.api.test._

102

import play.api.test.Helpers._

103

import play.api.mvc._

104

105

class RouteTestSpec extends PlaySpec with OneServerPerSuite {

106

107

override def fakeApplication(): Application = {

108

new GuiceApplicationBuilder()

109

.router(Router.from {

110

case GET(p"/api/hello") => Action {

111

Ok(Json.obj("message" -> "Hello World"))

112

}

113

case POST(p"/api/echo") => Action(parse.json) { request =>

114

Ok(request.body)

115

}

116

})

117

.build()

118

}

119

120

"API routes" should {

121

"handle GET request" in {

122

implicit val client = app.injector.instanceOf[WSClient]

123

124

// Test using route call

125

val call = controllers.routes.ApiController.hello() // Assuming you have this route

126

val response = await(WsTestClient.wsCall(call).get())

127

128

response.status mustBe OK

129

(response.json \ "message").as[String] mustBe "Hello World"

130

}

131

132

"handle POST request with JSON" in {

133

implicit val client = app.injector.instanceOf[WSClient]

134

135

val testData = Json.obj("name" -> "John", "age" -> 30)

136

val response = await(WsTestClient.wsUrl("/api/echo").post(testData))

137

138

response.status mustBe OK

139

response.json mustEqual testData

140

}

141

}

142

}

143

```

144

145

### Mock HTTP Server Testing

146

147

Create mock servers for testing external API interactions.

148

149

```scala

150

import play.core.server.Server

151

import play.api.routing.sird._

152

import play.api.mvc._

153

import play.api.test._

154

import scala.concurrent.ExecutionContext.Implicits.global

155

156

class ExternalApiSpec extends PlaySpec {

157

158

"External API client" should {

159

"handle mock server responses" in {

160

// Create mock server

161

Server.withRouter() {

162

case GET(p"/api/users/$id") => Action {

163

Ok(Json.obj(

164

"id" -> id,

165

"name" -> s"User $id",

166

"email" -> s"user$id@example.com"

167

))

168

}

169

case POST(p"/api/users") => Action(parse.json) { request =>

170

val name = (request.body \ "name").as[String]

171

Created(Json.obj(

172

"id" -> 123,

173

"name" -> name,

174

"email" -> s"${name.toLowerCase}@example.com"

175

))

176

}

177

} { implicit port =>

178

WsTestClient.withClient { client =>

179

// Test GET request

180

val getResponse = await(client.url(s"http://localhost:$port/api/users/1").get())

181

getResponse.status mustBe OK

182

(getResponse.json \ "name").as[String] mustBe "User 1"

183

184

// Test POST request

185

val postData = Json.obj("name" -> "Alice")

186

val postResponse = await(client.url(s"http://localhost:$port/api/users").post(postData))

187

postResponse.status mustBe CREATED

188

(postResponse.json \ "id").as[Int] mustBe 123

189

}

190

}

191

}

192

}

193

}

194

```

195

196

### Authentication Testing

197

198

Test OAuth and other authentication mechanisms.

199

200

```scala

201

import play.api.test._

202

import play.api.libs.oauth._

203

204

class OAuthTestSpec extends PlaySpec with OneServerPerSuite {

205

206

"OAuth integration" should {

207

"handle OAuth flow" in {

208

implicit val client = app.injector.instanceOf[WSClient]

209

210

// Mock OAuth service info

211

val serviceInfo = ServiceInfo(

212

requestTokenURL = s"http://localhost:$port/oauth/request_token",

213

accessTokenURL = s"http://localhost:$port/oauth/access_token",

214

authorizationURL = s"http://localhost:$port/oauth/authorize",

215

key = ConsumerKey("test_key", "test_secret")

216

)

217

218

val oauth = OAuth(serviceInfo)

219

220

// Test would require mock OAuth endpoints

221

// This is a simplified example structure

222

oauth.retrieveRequestToken("http://callback") match {

223

case Right(token) =>

224

token.token must not be empty

225

case Left(error) =>

226

fail(s"OAuth request failed: $error")

227

}

228

}

229

}

230

}

231

```

232

233

### Error Handling Testing

234

235

Test error conditions and edge cases.

236

237

```scala

238

class ErrorHandlingSpec extends PlaySpec with OneServerPerSuite {

239

240

"Error handling" should {

241

"handle 404 responses" in {

242

implicit val client = app.injector.instanceOf[WSClient]

243

244

val response = await(WsTestClient.wsUrl("/nonexistent").get())

245

response.status mustBe NOT_FOUND

246

}

247

248

"handle connection timeouts" in {

249

WsTestClient.withClient { client =>

250

val request = client.url("http://10.255.255.1:12345/timeout")

251

.withRequestTimeout(1000) // 1 second timeout

252

253

// This should timeout quickly

254

intercept[java.util.concurrent.TimeoutException] {

255

await(request.get())

256

}

257

}

258

}

259

260

"handle invalid JSON responses gracefully" in {

261

Server.withRouter() {

262

case GET(p"/invalid-json") => Action {

263

Ok("{ invalid json }")

264

}

265

} { implicit port =>

266

WsTestClient.withClient { client =>

267

val response = await(client.url(s"http://localhost:$port/invalid-json").get())

268

response.status mustBe OK

269

270

intercept[com.fasterxml.jackson.core.JsonParseException] {

271

response.json

272

}

273

}

274

}

275

}

276

}

277

}

278

```

279

280

### Streaming Response Testing

281

282

Test streaming responses and large data handling.

283

284

```scala

285

import play.api.libs.iteratee._

286

import akka.util.ByteString

287

288

class StreamingSpec extends PlaySpec with OneServerPerSuite {

289

290

"Streaming responses" should {

291

"handle large responses" in {

292

Server.withRouter() {

293

case GET(p"/large-data") => Action {

294

val largeData = "x" * 1000000 // 1MB of data

295

Ok(largeData)

296

}

297

} { implicit port =>

298

WsTestClient.withClient { client =>

299

val response = await(client.url(s"http://localhost:$port/large-data").stream())

300

301

response match {

302

case (headers, body) =>

303

headers.status mustBe OK

304

305

// Consume the stream

306

val consumed = body |>>> Iteratee.consume[Array[Byte]]()

307

val data = await(consumed)

308

data.length mustBe 1000000

309

}

310

}

311

}

312

}

313

}

314

}

315

```

316

317

### Custom Test Configurations

318

319

Create custom WS client configurations for testing.

320

321

```scala

322

import play.api.libs.ws.ning._

323

import scala.concurrent.duration._

324

325

class CustomConfigSpec extends PlaySpec {

326

327

"Custom WS configuration" should {

328

"use test-specific timeouts" in {

329

val testConfig = NingWSClientConfig(

330

wsClientConfig = WSClientConfig(

331

connectionTimeout = 5.seconds,

332

requestTimeout = 10.seconds,

333

followRedirects = false

334

)

335

)

336

337

val client = NingWSClient(testConfig)

338

339

try {

340

// Use client for testing with custom configuration

341

val response = await(client.url("https://httpbin.org/delay/2").get())

342

response.status mustBe OK

343

} finally {

344

client.close()

345

}

346

}

347

}

348

}

349

```

350

351

### Integration Test Setup

352

353

Complete setup for integration testing with WS client.

354

355

```scala

356

import org.scalatestplus.play._

357

import play.api.test._

358

import play.api.inject.guice.GuiceApplicationBuilder

359

360

class IntegrationTestSpec extends PlaySpec with OneServerPerSuite with OneBrowserPerSuite {

361

362

override def fakeApplication(): Application = {

363

new GuiceApplicationBuilder()

364

.configure(

365

"play.ws.timeout.connection" -> "10s",

366

"play.ws.timeout.idle" -> "10s",

367

"play.ws.timeout.request" -> "10s"

368

)

369

.build()

370

}

371

372

"Full application" should {

373

"handle complete user workflow" in {

374

implicit val client = app.injector.instanceOf[WSClient]

375

376

// Step 1: Create user

377

val createUser = Json.obj(

378

"name" -> "Test User",

379

"email" -> "test@example.com"

380

)

381

382

val createResponse = await(WsTestClient.wsUrl("/api/users").post(createUser))

383

createResponse.status mustBe CREATED

384

385

val userId = (createResponse.json \ "id").as[Long]

386

387

// Step 2: Retrieve user

388

val getResponse = await(WsTestClient.wsUrl(s"/api/users/$userId").get())

389

getResponse.status mustBe OK

390

391

val user = getResponse.json

392

(user \ "name").as[String] mustBe "Test User"

393

(user \ "email").as[String] mustBe "test@example.com"

394

395

// Step 3: Update user

396

val updateData = Json.obj("name" -> "Updated User")

397

val updateResponse = await(WsTestClient.wsUrl(s"/api/users/$userId").put(updateData))

398

updateResponse.status mustBe OK

399

400

// Step 4: Verify update

401

val verifyResponse = await(WsTestClient.wsUrl(s"/api/users/$userId").get())

402

(verifyResponse.json \ "name").as[String] mustBe "Updated User"

403

404

// Step 5: Delete user

405

val deleteResponse = await(WsTestClient.wsUrl(s"/api/users/$userId").delete())

406

deleteResponse.status mustBe NO_CONTENT

407

}

408

}

409

}

410

```

411

412

### Performance Testing

413

414

Basic performance testing with WS client.

415

416

```scala

417

import scala.concurrent.Future

418

import scala.concurrent.ExecutionContext.Implicits.global

419

420

class PerformanceSpec extends PlaySpec with OneServerPerSuite {

421

422

"Performance tests" should {

423

"handle concurrent requests" in {

424

implicit val client = app.injector.instanceOf[WSClient]

425

426

val concurrentRequests = 10

427

val requests = (1 to concurrentRequests).map { i =>

428

WsTestClient.wsUrl(s"/api/users/$i").get()

429

}

430

431

val startTime = System.currentTimeMillis()

432

val responses = await(Future.sequence(requests))

433

val endTime = System.currentTimeMillis()

434

435

responses.length mustBe concurrentRequests

436

responses.foreach(_.status mustBe OK)

437

438

val totalTime = endTime - startTime

439

println(s"$concurrentRequests requests completed in ${totalTime}ms")

440

441

// Assert reasonable performance (adjust threshold as needed)

442

totalTime must be < 5000L // Less than 5 seconds

443

}

444

}

445

}

446

```