From 875e7f8630bfa3410bb10849a4d3e298ece32d74 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 13 May 2020 12:27:42 +0100 Subject: [PATCH] Match multiple values in HeaderAssertions Closes gh-23878 --- .../web/reactive/server/HeaderAssertions.java | 67 +++++++++++++++++-- .../reactive/server/HeaderAssertionTests.java | 43 ++++++++++-- 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java index 4527fbdf08..efb0d904ec 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/HeaderAssertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.test.web.reactive.server; import java.util.Arrays; +import java.util.List; import java.util.function.Consumer; import org.hamcrest.Matcher; @@ -28,6 +29,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.lang.Nullable; import org.springframework.test.util.AssertionErrors; +import org.springframework.util.CollectionUtils; /** * Assertions on headers of the response. @@ -70,6 +72,33 @@ public class HeaderAssertions { return this.responseSpec; } + /** + * Match all values of the response header with the given regex + * patterns which are applied to the values of the header in the + * same order. Note that the number of pattenrs must match the + * number of actual values. + * @param name the header name + * @param patterns one or more regex patterns, one per expected value + * @since 5.3 + */ + public WebTestClient.ResponseSpec valuesMatch(String name, String... patterns) { + this.exchangeResult.assertWithDiagnostics(() -> { + List values = getRequiredValues(name); + AssertionErrors.assertTrue( + getMessage(name) + " has fewer or more values " + values + + " than number of patterns to match with " + Arrays.toString(patterns), + values.size() == patterns.length); + for (int i = 0; i < values.size(); i++) { + String value = values.get(i); + String pattern = patterns[i]; + AssertionErrors.assertTrue( + getMessage(name) + "[" + i + "]='" + value + "' does not match '" + pattern + "'", + value.matches(pattern)); + } + }); + return this.responseSpec; + } + /** * Assert the first value of the response header with a Hamcrest {@link Matcher}. * @param name the header name @@ -83,7 +112,19 @@ public class HeaderAssertions { } /** - * Consume the first value of the response header. + * Assert all values of the response header with a Hamcrest {@link Matcher}. + * @param name the header name + * @param matcher the matcher to use + * @since 5.3 + */ + public WebTestClient.ResponseSpec values(String name, Matcher> matcher) { + List values = getRequiredValues(name); + this.exchangeResult.assertWithDiagnostics(() -> MatcherAssert.assertThat(values, matcher)); + return this.responseSpec; + } + + /** + * Consume the first value of the named response header. * @param name the header name * @param consumer the consumer to use * @since 5.1 @@ -94,12 +135,28 @@ public class HeaderAssertions { return this.responseSpec; } + /** + * Consume all values of the named response header. + * @param name the header name + * @param consumer the consumer to use + * @since 5.3 + */ + public WebTestClient.ResponseSpec values(String name, Consumer> consumer) { + List values = getRequiredValues(name); + this.exchangeResult.assertWithDiagnostics(() -> consumer.accept(values)); + return this.responseSpec; + } + private String getRequiredValue(String name) { - String value = getHeaders().getFirst(name); - if (value == null) { + return getRequiredValues(name).get(0); + } + + private List getRequiredValues(String name) { + List values = getHeaders().get(name); + if (CollectionUtils.isEmpty(values)) { AssertionErrors.fail(getMessage(name) + " not found"); } - return value; + return values; } /** diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/HeaderAssertionTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/HeaderAssertionTests.java index d77fffae4a..0cf5fd31f8 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/HeaderAssertionTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/HeaderAssertionTests.java @@ -36,6 +36,7 @@ import org.springframework.mock.http.client.reactive.MockClientHttpResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItems; import static org.mockito.Mockito.mock; /** @@ -97,11 +98,33 @@ public class HeaderAssertionTests { assertions.valueMatches("Content-Type", ".*UTF-8.*"); // Wrong pattern - assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> - assertions.valueMatches("Content-Type", ".*ISO-8859-1.*")) - .satisfies(ex -> assertThat(ex.getCause()).hasMessage("Response header " + - "'Content-Type'=[application/json;charset=UTF-8] does not match " + - "[.*ISO-8859-1.*]")); + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> assertions.valueMatches("Content-Type", ".*ISO-8859-1.*")) + .satisfies(ex -> assertThat(ex.getCause()).hasMessage("Response header " + + "'Content-Type'=[application/json;charset=UTF-8] does not match " + + "[.*ISO-8859-1.*]")); + } + + @Test + public void valuesMatch() { + HttpHeaders headers = new HttpHeaders(); + headers.add("foo", "value1"); + headers.add("foo", "value2"); + headers.add("foo", "value3"); + HeaderAssertions assertions = headerAssertions(headers); + + assertions.valuesMatch("foo", "val.*1", "val.*2", "val.*3"); + + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> assertions.valuesMatch("foo", ".*", "val.*5")) + .satisfies(ex -> assertThat(ex.getCause()).hasMessage( + "Response header 'foo' has fewer or more values [value1, value2, value3] " + + "than number of patterns to match with [.*, val.*5]")); + + assertThatExceptionOfType(AssertionError.class) + .isThrownBy(() -> assertions.valuesMatch("foo", ".*", "val.*5", ".*")) + .satisfies(ex -> assertThat(ex.getCause()).hasMessage( + "Response header 'foo'[1]='value2' does not match 'val.*5'")); } @Test @@ -113,6 +136,16 @@ public class HeaderAssertionTests { assertions.value("foo", containsString("a")); } + @Test + public void valuesMatcher() { + HttpHeaders headers = new HttpHeaders(); + headers.add("foo", "bar"); + headers.add("foo", "baz"); + HeaderAssertions assertions = headerAssertions(headers); + + assertions.values("foo", hasItems("bar", "baz")); + } + @Test public void exists() { HttpHeaders headers = new HttpHeaders();