Skip to content

Commit 4a74e1f

Browse files
committed
Add AssertJ support for MockMvc
Closes gh-21178
2 parents 214a54d + e7d7cb8 commit 4a74e1f

File tree

56 files changed

+7023
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+7023
-0
lines changed

Diff for: spring-test/spring-test.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies {
3232
optional("org.apache.groovy:groovy")
3333
optional("org.apache.tomcat.embed:tomcat-embed-core")
3434
optional("org.aspectj:aspectjweaver")
35+
optional("org.assertj:assertj-core")
3536
optional("org.hamcrest:hamcrest")
3637
optional("org.htmlunit:htmlunit") {
3738
exclude group: "commons-logging", module: "commons-logging"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.http;
18+
19+
import java.time.Instant;
20+
import java.time.ZoneId;
21+
import java.time.ZonedDateTime;
22+
import java.time.temporal.ChronoUnit;
23+
import java.util.List;
24+
25+
import org.assertj.core.api.AbstractMapAssert;
26+
import org.assertj.core.api.Assertions;
27+
28+
import org.springframework.http.HttpHeaders;
29+
30+
/**
31+
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied to
32+
* {@link HttpHeaders}.
33+
*
34+
* @author Stephane Nicoll
35+
* @since 6.2
36+
*/
37+
public class HttpHeadersAssert extends AbstractMapAssert<HttpHeadersAssert, HttpHeaders, String, List<String>> {
38+
39+
private static final ZoneId GMT = ZoneId.of("GMT");
40+
41+
42+
public HttpHeadersAssert(HttpHeaders actual) {
43+
super(actual, HttpHeadersAssert.class);
44+
as("HTTP headers");
45+
}
46+
47+
/**
48+
* Verify that the actual HTTP headers contain a header with the given
49+
* {@code name}.
50+
* @param name the name of an expected HTTP header
51+
* @see #containsKey
52+
*/
53+
public HttpHeadersAssert containsHeader(String name) {
54+
return containsKey(name);
55+
}
56+
57+
/**
58+
* Verify that the actual HTTP headers contain the headers with the given
59+
* {@code names}.
60+
* @param names the names of expected HTTP headers
61+
* @see #containsKeys
62+
*/
63+
public HttpHeadersAssert containsHeaders(String... names) {
64+
return containsKeys(names);
65+
}
66+
67+
/**
68+
* Verify that the actual HTTP headers do not contain a header with the
69+
* given {@code name}.
70+
* @param name the name of an HTTP header that should not be present
71+
* @see #doesNotContainKey
72+
*/
73+
public HttpHeadersAssert doesNotContainsHeader(String name) {
74+
return doesNotContainKey(name);
75+
}
76+
77+
/**
78+
* Verify that the actual HTTP headers do not contain any of the headers
79+
* with the given {@code names}.
80+
* @param names the names of HTTP headers that should not be present
81+
* @see #doesNotContainKeys
82+
*/
83+
public HttpHeadersAssert doesNotContainsHeaders(String... names) {
84+
return doesNotContainKeys(names);
85+
}
86+
87+
/**
88+
* Verify that the actual HTTP headers contain a header with the given
89+
* {@code name} and {@link String} {@code value}.
90+
* @param name the name of the cookie
91+
* @param value the expected value of the header
92+
*/
93+
public HttpHeadersAssert hasValue(String name, String value) {
94+
containsKey(name);
95+
Assertions.assertThat(this.actual.getFirst(name))
96+
.as("check primary value for HTTP header '%s'", name)
97+
.isEqualTo(value);
98+
return this.myself;
99+
}
100+
101+
/**
102+
* Verify that the actual HTTP headers contain a header with the given
103+
* {@code name} and {@link Long} {@code value}.
104+
* @param name the name of the cookie
105+
* @param value the expected value of the header
106+
*/
107+
public HttpHeadersAssert hasValue(String name, long value) {
108+
containsKey(name);
109+
Assertions.assertThat(this.actual.getFirst(name))
110+
.as("check primary long value for HTTP header '%s'", name)
111+
.asLong().isEqualTo(value);
112+
return this.myself;
113+
}
114+
115+
/**
116+
* Verify that the actual HTTP headers contain a header with the given
117+
* {@code name} and {@link Instant} {@code value}.
118+
* @param name the name of the cookie
119+
* @param value the expected value of the header
120+
*/
121+
public HttpHeadersAssert hasValue(String name, Instant value) {
122+
containsKey(name);
123+
Assertions.assertThat(this.actual.getFirstZonedDateTime(name))
124+
.as("check primary date value for HTTP header '%s'", name)
125+
.isCloseTo(ZonedDateTime.ofInstant(value, GMT), Assertions.within(999, ChronoUnit.MILLIS));
126+
return this.myself;
127+
}
128+
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.http;
18+
19+
import org.assertj.core.api.AbstractObjectAssert;
20+
import org.assertj.core.api.Assertions;
21+
import org.assertj.core.error.BasicErrorMessageFactory;
22+
import org.assertj.core.internal.Failures;
23+
24+
import org.springframework.http.InvalidMediaTypeException;
25+
import org.springframework.http.MediaType;
26+
import org.springframework.lang.Nullable;
27+
import org.springframework.util.StringUtils;
28+
29+
/**
30+
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied
31+
* to a {@link MediaType}.
32+
*
33+
* @author Brian Clozel
34+
* @author Stephane Nicoll
35+
* @since 6.2
36+
*/
37+
public class MediaTypeAssert extends AbstractObjectAssert<MediaTypeAssert, MediaType> {
38+
39+
public MediaTypeAssert(@Nullable MediaType mediaType) {
40+
super(mediaType, MediaTypeAssert.class);
41+
as("Media type");
42+
}
43+
44+
public MediaTypeAssert(@Nullable String actual) {
45+
this(StringUtils.hasText(actual) ? MediaType.parseMediaType(actual) : null);
46+
}
47+
48+
/**
49+
* Verify that the actual media type is equal to the given string
50+
* representation.
51+
* @param expected the expected media type
52+
*/
53+
public MediaTypeAssert isEqualTo(String expected) {
54+
return isEqualTo(parseMediaType(expected));
55+
}
56+
57+
/**
58+
* Verify that the actual media type is
59+
* {@linkplain MediaType#isCompatibleWith(MediaType) compatible} with the
60+
* given one. Example: <pre><code class='java'>
61+
* // Check that actual is compatible with "application/json"
62+
* assertThat(mediaType).isCompatibleWith(MediaType.APPLICATION_JSON);
63+
* </code></pre>
64+
* @param mediaType the media type with which to compare
65+
*/
66+
public MediaTypeAssert isCompatibleWith(MediaType mediaType) {
67+
Assertions.assertThat(this.actual)
68+
.withFailMessage("Expecting null to be compatible with '%s'", mediaType).isNotNull();
69+
Assertions.assertThat(mediaType)
70+
.withFailMessage("Expecting '%s' to be compatible with null", this.actual).isNotNull();
71+
Assertions.assertThat(this.actual.isCompatibleWith(mediaType))
72+
.as("check media type '%s' is compatible with '%s'", this.actual.toString(), mediaType.toString())
73+
.isTrue();
74+
return this;
75+
}
76+
77+
/**
78+
* Verify that the actual media type is
79+
* {@linkplain MediaType#isCompatibleWith(MediaType) compatible} with the
80+
* given one. Example: <pre><code class='java'>
81+
* // Check that actual is compatible with "text/plain"
82+
* assertThat(mediaType).isCompatibleWith("text/plain");
83+
* </code></pre>
84+
* @param mediaType the media type with which to compare
85+
*/
86+
public MediaTypeAssert isCompatibleWith(String mediaType) {
87+
return isCompatibleWith(parseMediaType(mediaType));
88+
}
89+
90+
91+
private MediaType parseMediaType(String value) {
92+
try {
93+
return MediaType.parseMediaType(value);
94+
}
95+
catch (InvalidMediaTypeException ex) {
96+
throw Failures.instance().failure(this.info, new ShouldBeValidMediaType(value, ex.getMessage()));
97+
}
98+
}
99+
100+
private static final class ShouldBeValidMediaType extends BasicErrorMessageFactory {
101+
102+
private ShouldBeValidMediaType(String mediaType, String errorMessage) {
103+
super("%nExpecting:%n %s%nTo be a valid media type but got:%n %s%n", mediaType, errorMessage);
104+
}
105+
}
106+
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Test support for HTTP concepts.
3+
*/
4+
@NonNullApi
5+
@NonNullFields
6+
package org.springframework.test.http;
7+
8+
import org.springframework.lang.NonNullApi;
9+
import org.springframework.lang.NonNullFields;

0 commit comments

Comments
 (0)