Allow MockRest to match header/queryParam value list with one Matcher

This commit adds a `header` variant and a `queryParam` variant to the
`MockRestRequestMatchers` API which take a single `Matcher` over the
list of values.

Contrary to the vararg variants, the whole list is evaluated and the
caller can choose the desired semantics using readily-available iterable
matchers like `everyItem`, `hasItems`, `hasSize`, `contains` or
`containsInAnyOrder`...

The fact that the previous variants don't strictly check the size of the
actual list == the number of provided matchers or expected values is
now documented in their respective javadocs.

See gh-28660
Closes gh-29953
This commit is contained in:
Simon Baslé
2023-02-13 17:51:01 +01:00
committed by GitHub
parent 79a1fcb099
commit 189d4e3e4c
2 changed files with 199 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -16,18 +16,35 @@
package org.springframework.test.web.client.match;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import org.springframework.mock.http.client.MockClientHttpRequest;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.any;
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;
/**
* Unit tests for {@link MockRestRequestMatchers}.
@@ -146,6 +163,63 @@ class MockRestRequestMatchersTests {
.hasMessageContaining("was \"bar\"");
}
@Test
void headerListMissing() {
assertThatThrownBy(() -> MockRestRequestMatchers.header("foo", hasSize(2)).match(this.request))
.isInstanceOf(AssertionError.class)
.hasMessage("No header values for header [foo]");
}
@Test
void headerListMatchers() throws IOException {
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
MockRestRequestMatchers.header("foo", containsInAnyOrder(endsWith("baz"), endsWith("bar"))).match(this.request);
MockRestRequestMatchers.header("foo", contains(is("bar"), is("baz"))).match(this.request);
MockRestRequestMatchers.header("foo", contains(is("bar"), Matchers.anything())).match(this.request);
MockRestRequestMatchers.header("foo", hasItem(endsWith("baz"))).match(this.request);
MockRestRequestMatchers.header("foo", everyItem(startsWith("ba"))).match(this.request);
MockRestRequestMatchers.header("foo", hasSize(2)).match(this.request);
//these can be a bit ambiguous when reading the test (the compiler selects the list matcher):
MockRestRequestMatchers.header("foo", notNullValue()).match(this.request);
MockRestRequestMatchers.header("foo", is(anything())).match(this.request);
MockRestRequestMatchers.header("foo", allOf(notNullValue(), notNullValue())).match(this.request);
//these are not as ambiguous thanks to an inner matcher that is either obviously list-oriented,
//string-oriented or obviously a vararg of matchers
//list matcher version
MockRestRequestMatchers.header("foo", allOf(notNullValue(), hasSize(2))).match(this.request);
//vararg version
MockRestRequestMatchers.header("foo", allOf(notNullValue(), endsWith("ar"))).match(this.request);
MockRestRequestMatchers.header("foo", is((any(String.class)))).match(this.request);
MockRestRequestMatchers.header("foo", CoreMatchers.either(is("bar")).or(is(nullValue()))).match(this.request);
MockRestRequestMatchers.header("foo", is(notNullValue()), is(notNullValue())).match(this.request);
}
@Test
void headerListContainsMismatch() {
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> MockRestRequestMatchers
.header("foo", contains(containsString("ba"))).match(this.request))
.withMessage("Request header values for [foo]\n"
+ "Expected: iterable containing [a string containing \"ba\"]\n"
+ " but: not matched: \"baz\"");
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> MockRestRequestMatchers
.header("foo", hasItem(endsWith("ba"))).match(this.request))
.withMessage("Request header values for [foo]\n"
+ "Expected: a collection containing a string ending with \"ba\"\n"
+ " but: mismatches were: [was \"bar\", was \"baz\"]");
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> MockRestRequestMatchers
.header("foo", everyItem(endsWith("ar"))).match(this.request))
.withMessage("Request header values for [foo]\n"
+ "Expected: every item is a string ending with \"ar\"\n"
+ " but: an item was \"baz\"");
}
@Test
void headers() throws Exception {
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
@@ -210,4 +284,62 @@ class MockRestRequestMatchersTests {
.hasMessageContaining("was \"bar\"");
}
@Test
void queryParamListMissing() {
assertThatThrownBy(() -> MockRestRequestMatchers.queryParam("foo", hasSize(2)).match(this.request))
.isInstanceOf(AssertionError.class)
.hasMessage("No queryParam [foo]");
}
@Test
void queryParamListMatchers() throws IOException {
this.request.setURI(URI.create("http://www.foo.example/a?foo=bar&foo=baz"));
MockRestRequestMatchers.queryParam("foo", containsInAnyOrder(endsWith("baz"), endsWith("bar"))).match(this.request);
MockRestRequestMatchers.queryParam("foo", contains(is("bar"), is("baz"))).match(this.request);
MockRestRequestMatchers.queryParam("foo", contains(is("bar"), Matchers.anything())).match(this.request);
MockRestRequestMatchers.queryParam("foo", hasItem(endsWith("baz"))).match(this.request);
MockRestRequestMatchers.queryParam("foo", everyItem(startsWith("ba"))).match(this.request);
MockRestRequestMatchers.queryParam("foo", hasSize(2)).match(this.request);
//these can be a bit ambiguous when reading the test (the compiler selects the list matcher):
MockRestRequestMatchers.queryParam("foo", notNullValue()).match(this.request);
MockRestRequestMatchers.queryParam("foo", is(anything())).match(this.request);
MockRestRequestMatchers.queryParam("foo", allOf(notNullValue(), notNullValue())).match(this.request);
//these are not as ambiguous thanks to an inner matcher that is either obviously list-oriented,
//string-oriented or obviously a vararg of matchers
//list matcher version
MockRestRequestMatchers.queryParam("foo", allOf(notNullValue(), hasSize(2))).match(this.request);
//vararg version
MockRestRequestMatchers.queryParam("foo", allOf(notNullValue(), endsWith("ar"))).match(this.request);
MockRestRequestMatchers.queryParam("foo", is((any(String.class)))).match(this.request);
MockRestRequestMatchers.queryParam("foo", CoreMatchers.either(is("bar")).or(is(nullValue()))).match(this.request);
MockRestRequestMatchers.queryParam("foo", is(notNullValue()), is(notNullValue())).match(this.request);
}
@Test
void queryParamListContainsMismatch() {
this.request.setURI(URI.create("http://www.foo.example/a?foo=bar&foo=baz"));
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> MockRestRequestMatchers
.queryParam("foo", contains(containsString("ba"))).match(this.request))
.withMessage("Request queryParam values for [foo]\n"
+ "Expected: iterable containing [a string containing \"ba\"]\n"
+ " but: not matched: \"baz\"");
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> MockRestRequestMatchers
.queryParam("foo", hasItem(endsWith("ba"))).match(this.request))
.withMessage("Request queryParam values for [foo]\n"
+ "Expected: a collection containing a string ending with \"ba\"\n"
+ " but: mismatches were: [was \"bar\", was \"baz\"]");
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> MockRestRequestMatchers
.queryParam("foo", everyItem(endsWith("ar"))).match(this.request))
.withMessage("Request queryParam values for [foo]\n"
+ "Expected: every item is a string ending with \"ar\"\n"
+ " but: an item was \"baz\"");
}
}