Skip to content

Commit d1a64e1

Browse files
committed
Refactor ExchangeResult
Clearly separate how the result of an exchange is represented before the response body has been read (e.g. assertions on status and headers only) vs later after the body is extracted to a representation (assertions on the extracted body) or is decoded to Flux<T> (e.g. for use with a StepVerifier).
1 parent 20be40b commit d1a64e1

File tree

8 files changed

+284
-180
lines changed

8 files changed

+284
-180
lines changed

spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java

Lines changed: 102 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -253,56 +253,76 @@ public <T, S extends Publisher<T>> ResponseSpec exchange(S publisher, Class<T> e
253253
return toResponseSpec(this.headerSpec.exchange(publisher, elementClass));
254254
}
255255

256-
private DefaultResponseSpec toResponseSpec(Mono<ClientResponse> responseMono) {
257-
ClientResponse response = responseMono.block(getTimeout());
258-
ClientHttpRequest request = webTestClientConnector.claimRequest(this.requestId);
259-
ExchangeResult<Flux<DataBuffer>> result = ExchangeResult.create(request, response);
260-
return new DefaultResponseSpec(result, response);
256+
private DefaultResponseSpec toResponseSpec(Mono<ClientResponse> mono) {
257+
ClientResponse response = mono.block(getTimeout());
258+
ClientHttpRequest httpRequest = webTestClientConnector.claimRequest(this.requestId);
259+
return new DefaultResponseSpec(httpRequest, response);
261260
}
262261
}
263262

264-
private abstract class ResponseSpecSupport {
265-
266-
private final ExchangeResult<Flux<DataBuffer>> exchangeResult;
263+
/**
264+
* ExchangeResult that contains the live {@link ClientResponse}.
265+
*/
266+
private class UndecodedExchangeResult extends ExchangeResult {
267267

268268
private final ClientResponse response;
269269

270270

271-
public ResponseSpecSupport(ExchangeResult<Flux<DataBuffer>> result, ClientResponse response) {
272-
this.exchangeResult = result;
271+
public UndecodedExchangeResult(ClientHttpRequest httpRequest, ClientResponse response) {
272+
super(httpRequest, response);
273273
this.response = response;
274274
}
275275

276276

277-
protected ExchangeResult<Flux<DataBuffer>> getExchangeResult() {
278-
return this.exchangeResult;
277+
public EntityExchangeResult<?> consumeSingle(ResolvableType elementType) {
278+
Object body = this.response.body(toMono(elementType)).block(getTimeout());
279+
return new EntityExchangeResult<>(this, body);
279280
}
280281

281-
protected ClientResponse getResponse() {
282-
return this.response;
282+
public EntityExchangeResult<List<?>> consumeList(ResolvableType elementType, int count) {
283+
Flux<?> flux = this.response.body(toFlux(elementType));
284+
if (count >= 0) {
285+
flux = flux.take(count);
286+
}
287+
List<?> body = flux.collectList().block(getTimeout());
288+
return new EntityExchangeResult<>(this, body);
283289
}
284290

285-
protected <T> ExchangeResult<T> createResultWithDecodedBody(T body) {
286-
return ExchangeResult.withDecodedBody(this.exchangeResult, body);
291+
public <T> FluxExchangeResult<T> decodeBody(ResolvableType elementType) {
292+
Flux<T> body = this.response.body(toFlux(elementType));
293+
return new FluxExchangeResult<>(this, body, elementType);
287294
}
288295

296+
@SuppressWarnings("unchecked")
297+
public EntityExchangeResult<Map<?, ?>> consumeMap(ResolvableType keyType, ResolvableType valueType) {
298+
ResolvableType mapType = ResolvableType.forClassWithGenerics(Map.class, keyType, valueType);
299+
return (EntityExchangeResult<Map<?, ?>>) consumeSingle(mapType);
300+
}
301+
302+
public EntityExchangeResult<Void> consumeEmpty() {
303+
DataBuffer buffer = this.response.body(toDataBuffers()).blockFirst(getTimeout());
304+
assertTrue("Expected empty body", buffer == null);
305+
return new EntityExchangeResult<>(this, null);
306+
}
289307
}
290308

291-
private class DefaultResponseSpec extends ResponseSpecSupport implements ResponseSpec {
309+
private class DefaultResponseSpec implements ResponseSpec {
292310

311+
private final UndecodedExchangeResult exchangeResult;
293312

294-
public DefaultResponseSpec(ExchangeResult<Flux<DataBuffer>> exchangeResult, ClientResponse response) {
295-
super(exchangeResult, response);
313+
314+
public DefaultResponseSpec(ClientHttpRequest httpRequest, ClientResponse response) {
315+
this.exchangeResult = new UndecodedExchangeResult(httpRequest, response);
296316
}
297317

298318
@Override
299319
public StatusAssertions expectStatus() {
300-
return new StatusAssertions(getExchangeResult(), this);
320+
return new StatusAssertions(this.exchangeResult, this);
301321
}
302322

303323
@Override
304324
public HeaderAssertions expectHeader() {
305-
return new HeaderAssertions(getExchangeResult(), this);
325+
return new HeaderAssertions(this.exchangeResult, this);
306326
}
307327

308328
@Override
@@ -312,40 +332,43 @@ public TypeBodySpec expectBody(Class<?> elementType) {
312332

313333
@Override
314334
public TypeBodySpec expectBody(ResolvableType elementType) {
315-
return new DefaultTypeBodySpec(this, elementType);
335+
return new DefaultTypeBodySpec(this.exchangeResult, elementType);
316336
}
317337

318338
@Override
319339
public BodySpec expectBody() {
320-
return new DefaultBodySpec(this);
340+
return new DefaultBodySpec(this.exchangeResult);
321341
}
322342

323343
@Override
324-
public ResponseSpec consumeWith(Consumer<ExchangeResult<Flux<DataBuffer>>> consumer) {
325-
consumer.accept(getExchangeResult());
344+
public ResponseSpec consumeWith(Consumer<ExchangeResult> consumer) {
345+
consumer.accept(this.exchangeResult);
326346
return this;
327347
}
328348

329349
@Override
330-
public ExchangeResult<Flux<DataBuffer>> returnResult() {
331-
return getExchangeResult();
350+
public ExchangeResult returnResult() {
351+
return this.exchangeResult;
332352
}
333353
}
334354

335-
private class DefaultTypeBodySpec extends ResponseSpecSupport implements TypeBodySpec {
355+
private class DefaultTypeBodySpec implements TypeBodySpec {
356+
357+
private final UndecodedExchangeResult exchangeResult;
336358

337359
private final ResolvableType elementType;
338360

339361

340-
public DefaultTypeBodySpec(DefaultResponseSpec spec, ResolvableType elementType) {
341-
super(spec.getExchangeResult(), spec.getResponse());
362+
public DefaultTypeBodySpec(UndecodedExchangeResult result, ResolvableType elementType) {
363+
this.exchangeResult = result;
342364
this.elementType = elementType;
343365
}
344366

345367

346368
@Override
347369
public SingleValueBodySpec value() {
348-
return new DefaultSingleValueBodySpec(this, this.elementType);
370+
EntityExchangeResult<?> completed = this.exchangeResult.consumeSingle(this.elementType);
371+
return new DefaultSingleValueBodySpec(completed);
349372
}
350373

351374
@Override
@@ -354,59 +377,52 @@ public ListBodySpec list() {
354377
}
355378

356379
@Override
357-
public ListBodySpec list(int elementCount) {
358-
return new DefaultListBodySpec(this, this.elementType, elementCount);
380+
public ListBodySpec list(int count) {
381+
EntityExchangeResult<List<?>> completed = this.exchangeResult.consumeList(this.elementType, count);
382+
return new DefaultListBodySpec(completed);
359383
}
360384

361385
@Override
362-
public <T> ExchangeResult<Flux<T>> returnResult() {
363-
Flux<T> flux = getResponse().body(toFlux(this.elementType));
364-
return createResultWithDecodedBody(flux);
386+
public <T> FluxExchangeResult<T> returnResult() {
387+
return this.exchangeResult.decodeBody(this.elementType);
365388
}
366389
}
367390

368-
private class DefaultSingleValueBodySpec extends ResponseSpecSupport implements SingleValueBodySpec {
391+
private class DefaultSingleValueBodySpec implements SingleValueBodySpec {
369392

370-
private final Object body;
393+
private final EntityExchangeResult<?> exchangeResult;
371394

372395

373-
public DefaultSingleValueBodySpec(DefaultTypeBodySpec spec, ResolvableType elementType) {
374-
super(spec.getExchangeResult(), spec.getResponse());
375-
this.body = getResponse().body(toMono(elementType)).block(getTimeout());
396+
public DefaultSingleValueBodySpec(EntityExchangeResult<?> result) {
397+
this.exchangeResult = result;
376398
}
377399

378400

379401
@Override
380-
public <T> ExchangeResult<T> isEqualTo(Object expected) {
381-
assertEquals("Response body", expected, this.body);
402+
public <T> EntityExchangeResult<T> isEqualTo(T expected) {
403+
assertEquals("Response body", expected, this.exchangeResult.getResponseBody());
382404
return returnResult();
383405
}
384406

385407
@Override
386-
@SuppressWarnings("unchecked")
387-
public <T> ExchangeResult<T> returnResult() {
388-
return createResultWithDecodedBody((T) this.body);
408+
public <T> EntityExchangeResult<T> returnResult() {
409+
return new EntityExchangeResult<>(this.exchangeResult, (T) this.exchangeResult.getResponseBody());
389410
}
390411
}
391412

392-
private class DefaultListBodySpec extends ResponseSpecSupport implements ListBodySpec {
413+
private class DefaultListBodySpec implements ListBodySpec {
393414

394-
private final List<?> body;
415+
private final EntityExchangeResult<List<?>> exchangeResult;
395416

396417

397-
public DefaultListBodySpec(DefaultTypeBodySpec spec, ResolvableType elementType, int elementCount) {
398-
super(spec.getExchangeResult(), spec.getResponse());
399-
Flux<?> flux = getResponse().body(toFlux(elementType));
400-
if (elementCount >= 0) {
401-
flux = flux.take(elementCount);
402-
}
403-
this.body = flux.collectList().block(getTimeout());
418+
public DefaultListBodySpec(EntityExchangeResult<List<?>> result) {
419+
this.exchangeResult = result;
404420
}
405421

406422

407423
@Override
408-
public <T> ExchangeResult<List<T>> isEqualTo(List<T> expected) {
409-
assertEquals("Response body", expected, this.body);
424+
public <T> EntityExchangeResult<List<T>> isEqualTo(List<T> expected) {
425+
assertEquals("Response body", expected, this.exchangeResult.getResponseBody());
410426
return returnResult();
411427
}
412428

@@ -419,38 +435,38 @@ public ListBodySpec hasSize(int size) {
419435
public ListBodySpec contains(Object... elements) {
420436
List<Object> elementList = Arrays.asList(elements);
421437
String message = "Response body does not contain " + elementList;
422-
assertTrue(message, this.body.containsAll(elementList));
438+
assertTrue(message, this.exchangeResult.getResponseBody().containsAll(elementList));
423439
return this;
424440
}
425441

426442
@Override
427443
public ListBodySpec doesNotContain(Object... elements) {
428444
List<Object> elementList = Arrays.asList(elements);
429445
String message = "Response body should have contained " + elementList;
430-
assertTrue(message, !this.body.containsAll(Arrays.asList(elements)));
446+
assertTrue(message, !this.exchangeResult.getResponseBody().containsAll(Arrays.asList(elements)));
431447
return this;
432448
}
433449

434450
@Override
435451
@SuppressWarnings("unchecked")
436-
public <T> ExchangeResult<List<T>> returnResult() {
437-
return createResultWithDecodedBody((List<T>) this.body);
452+
public <T> EntityExchangeResult<List<T>> returnResult() {
453+
return new EntityExchangeResult<>(this.exchangeResult, (List<T>) this.exchangeResult.getResponseBody());
438454
}
439455
}
440456

441-
private class DefaultBodySpec extends ResponseSpecSupport implements BodySpec {
457+
private class DefaultBodySpec implements BodySpec {
458+
459+
private final UndecodedExchangeResult exchangeResult;
442460

443461

444-
public DefaultBodySpec(DefaultResponseSpec spec) {
445-
super(spec.getExchangeResult(), spec.getResponse());
462+
public DefaultBodySpec(UndecodedExchangeResult result) {
463+
this.exchangeResult = result;
446464
}
447465

448466

449467
@Override
450-
public ExchangeResult<Void> isEmpty() {
451-
DataBuffer buffer = getResponse().body(toDataBuffers()).blockFirst(getTimeout());
452-
assertTrue("Expected empty body", buffer == null);
453-
return createResultWithDecodedBody(null);
468+
public EntityExchangeResult<Void> isEmpty() {
469+
return this.exchangeResult.consumeEmpty();
454470
}
455471

456472
@Override
@@ -460,57 +476,61 @@ public MapBodySpec map(Class<?> keyType, Class<?> valueType) {
460476

461477
@Override
462478
public MapBodySpec map(ResolvableType keyType, ResolvableType valueType) {
463-
return new DefaultMapBodySpec(this, keyType, valueType);
479+
EntityExchangeResult<Map<?, ?>> completed = this.exchangeResult.consumeMap(keyType, valueType);
480+
return new DefaultMapBodySpec(completed);
464481
}
465482
}
466483

467-
private class DefaultMapBodySpec extends ResponseSpecSupport implements MapBodySpec {
484+
private class DefaultMapBodySpec implements MapBodySpec {
468485

469-
private final Map<?, ?> body;
486+
private final EntityExchangeResult<Map<?, ?>> exchangeResult;
470487

471488

472-
public DefaultMapBodySpec(DefaultBodySpec spec, ResolvableType keyType, ResolvableType valueType) {
473-
super(spec.getExchangeResult(), spec.getResponse());
474-
ResolvableType mapType = ResolvableType.forClassWithGenerics(Map.class, keyType, valueType);
475-
this.body = (Map<?, ?>) spec.getResponse().body(toMono(mapType)).block(getTimeout());
489+
public DefaultMapBodySpec(EntityExchangeResult<Map<?, ?>> result) {
490+
this.exchangeResult = result;
476491
}
477492

478493

494+
private Map<?, ?> getBody() {
495+
return this.exchangeResult.getResponseBody();
496+
}
497+
479498
@Override
480-
public <K, V> ExchangeResult<Map<K, V>> isEqualTo(Map<K, V> expected) {
499+
public <K, V> EntityExchangeResult<Map<K, V>> isEqualTo(Map<K, V> expected) {
500+
assertEquals("Response body map", expected, getBody());
481501
return returnResult();
482502
}
483503

484504
@Override
485505
public MapBodySpec hasSize(int size) {
486-
assertEquals("Response body map size", size, this.body.size());
506+
assertEquals("Response body map size", size, getBody().size());
487507
return this;
488508
}
489509

490510
@Override
491511
public MapBodySpec contains(Object key, Object value) {
492-
assertEquals("Response body map value for key " + key, value, this.body.get(key));
512+
assertEquals("Response body map value for key " + key, value, getBody().get(key));
493513
return this;
494514
}
495515

496516
@Override
497517
public MapBodySpec containsKeys(Object... keys) {
498-
List<?> missing = Arrays.stream(keys).filter(k -> !this.body.containsKey(k)).collect(toList());
518+
List<?> missing = Arrays.stream(keys).filter(k -> !getBody().containsKey(k)).collect(toList());
499519
assertTrue("Response body map does not contain keys " + missing, missing.isEmpty());
500520
return this;
501521
}
502522

503523
@Override
504524
public MapBodySpec containsValues(Object... values) {
505-
List<?> missing = Arrays.stream(values).filter(v -> !this.body.containsValue(v)).collect(toList());
525+
List<?> missing = Arrays.stream(values).filter(v -> !getBody().containsValue(v)).collect(toList());
506526
assertTrue("Response body map does not contain values " + missing, missing.isEmpty());
507527
return this;
508528
}
509529

510530
@Override
511531
@SuppressWarnings("unchecked")
512-
public <K, V> ExchangeResult<Map<K, V>> returnResult() {
513-
return createResultWithDecodedBody((Map<K, V>) this.body);
532+
public <K, V> EntityExchangeResult<Map<K, V>> returnResult() {
533+
return new EntityExchangeResult<>(this.exchangeResult, (Map<K, V>) getBody());
514534
}
515535
}
516536

0 commit comments

Comments
 (0)