Skip to content

Commit

Permalink
Move response body directly in AbstractMockHttpServletResponseAssert
Browse files Browse the repository at this point in the history
This commit removes ResponseBodyAssert and rather offers first-class
access support for the response body at the root level using bodyText(),
bodyJson(), and body().

This avoids a double navigation to assert the response body.

See gh-32712
  • Loading branch information
snicoll committed May 7, 2024
1 parent 24cc776 commit 5567d14
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 299 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@
* assertions} on the value.
*
* <p>Also support comparing the JSON document against a target, using
* {@linkplain JSONCompare JSON Assert}.
* {@linkplain JSONCompare JSON Assert}. Resources that are loaded from
* the classpath can be relative if a {@linkplain #withResourceLoadClass(Class)
* class} is provided. By default, {@code UTF-8} is used to load resources
* but this can be overridden using {@link #withCharset(Charset)}.
*
* @author Stephane Nicoll
* @author Phillip Webb
Expand All @@ -71,28 +74,27 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
@Nullable
private final GenericHttpMessageConverter<Object> jsonMessageConverter;

private final JsonLoader jsonLoader;
@Nullable
private Class<?> resourceLoadClass;

@Nullable
private Charset charset;

private JsonLoader jsonLoader;

/**
* Create an assert for the given JSON document.
* <p>Path can be converted to a value object using the given
* {@linkplain GenericHttpMessageConverter json message converter}.
* <p>Resources to match can be loaded relative to the given
* {@code resourceLoadClass}. If not specified, resources must always be
* absolute. A specific {@link Charset} can be provided if {@code UTF-8} is
* not suitable.
* @param json the JSON document to assert
* @param jsonMessageConverter the converter to use
* @param resourceLoadClass the class used to load resources
* @param charset the charset of the JSON resources
* @param selfType the implementation type of this assert
*/
protected AbstractJsonContentAssert(@Nullable String json,
@Nullable GenericHttpMessageConverter<Object> jsonMessageConverter, @Nullable Class<?> resourceLoadClass,
@Nullable Charset charset, Class<?> selfType) {
@Nullable GenericHttpMessageConverter<Object> jsonMessageConverter, Class<?> selfType) {
super(json, selfType);
this.jsonMessageConverter = jsonMessageConverter;
this.jsonLoader = new JsonLoader(resourceLoadClass, charset);
this.jsonLoader = new JsonLoader(null, null);
as("JSON content");
}

Expand Down Expand Up @@ -376,6 +378,31 @@ public SELF isNotStrictlyEqualTo(Resource expected) {
return isNotEqualTo(expected, JSONCompareMode.STRICT);
}

/**
* Override the class used to load resources. Resources can be loaded from
* an absolute location or relative to tpe specified class. For instance,
* specifying {@code com.example.MyClass} as the resource class allows you
* to use "my-file.json" to load {@code /com/example/my-file.json}.
* @param resourceLoadClass the class used to load resources or {@code null}
* to only use absolute paths.
*/
public SELF withResourceLoadClass(@Nullable Class<?> resourceLoadClass) {
this.resourceLoadClass = resourceLoadClass;
this.jsonLoader = new JsonLoader(resourceLoadClass, this.charset);
return this.myself;
}

/**
* Override the {@link Charset} to use to load resources. By default,
* resources are loaded using {@code UTF-8}.
* @param charset the charset to use, or {@code null} to use the default
*/
public SELF withCharset(@Nullable Charset charset) {
this.charset = charset;
this.jsonLoader = new JsonLoader(this.resourceLoadClass, charset);
return this.myself;
}


private JSONCompareResult compare(@Nullable CharSequence expectedJson, JSONCompareMode compareMode) {
return compare(this.actual, expectedJson, (actualJsonString, expectedJsonString) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public final class JsonContent implements AssertProvider<JsonContentAssert> {
*/
@Override
public JsonContentAssert assertThat() {
return new JsonContentAssert(this.json, null, this.resourceLoadClass, null);
return new JsonContentAssert(this.json, null).withResourceLoadClass(this.resourceLoadClass);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package org.springframework.test.json;

import java.nio.charset.Charset;

import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.lang.Nullable;

Expand All @@ -33,19 +31,11 @@ public class JsonContentAssert extends AbstractJsonContentAssert<JsonContentAsse
* Create an assert for the given JSON document.
* <p>Path can be converted to a value object using the given
* {@linkplain GenericHttpMessageConverter json message converter}.
* <p>Resources to match can be loaded relative to the given
* {@code resourceLoadClass}. If not specified, resources must always be
* absolute. A specific {@link Charset} can be provided if {@code UTF-8} is
* not suitable.
* @param json the JSON document to assert
* @param jsonMessageConverter the converter to use
* @param resourceLoadClass the class used to load resources
* @param charset the charset of the JSON resources
*/
public JsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter,
@Nullable Class<?> resourceLoadClass, @Nullable Charset charset) {

super(json, jsonMessageConverter, resourceLoadClass, charset, JsonContentAssert.class);
public JsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
super(json, jsonMessageConverter, JsonContentAssert.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@

import java.nio.charset.Charset;

import org.assertj.core.api.AbstractByteArrayAssert;
import org.assertj.core.api.AbstractStringAssert;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.ByteArrayAssert;

import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.json.AbstractJsonContentAssert;
import org.springframework.test.json.JsonContentAssert;
import org.springframework.test.web.UriAssert;

/**
Expand All @@ -45,22 +52,62 @@ protected AbstractMockHttpServletResponseAssert(
this.jsonMessageConverter = jsonMessageConverter;
}


/**
* Return a new {@linkplain ResponseBodyAssert assertion} object that uses
* the response body as the object to test. The returned assertion object
* provides access to the raw byte array, a String value decoded using the
* response's character encoding, and dedicated JSON testing support.
* Return a new {@linkplain AbstractStringAssert assertion} object that uses
* the response body converted to text as the object to test.
* <p>Examples: <pre><code class='java'>
* // Check that the response body is equal to "Hello World":
* assertThat(response).body().isEqualTo("Hello World");
* assertThat(response).bodyText().isEqualTo("Hello World");
* </code></pre>
*/
public AbstractStringAssert<?> bodyText() {
return Assertions.assertThat(readBody());
}

/**
* Return a new {@linkplain AbstractJsonContentAssert assertion} object that
* uses the response body converted to text as the object to test. Compared
* to {@link #bodyText()}, the assertion object provides dedicated JSON
* support.
* <p>Examples: <pre><code class='java'>
* // Check that the response body is strictly equal to the content of
* // "/com/acme/sample/person-created.json":
* assertThat(response).bodyJson()
* .isStrictlyEqualToJson("/com/acme/sample/person-created.json");
*
* // Check that the response is strictly equal to the content of the
* // specified file located in the same package as the PersonController:
* assertThat(response).bodyJson().withResourceLoadClass(PersonController.class)
* .isStrictlyEqualToJson("person-created.json");
* </code></pre>
* The returned assert object also supports JSON path expressions.
* <p>Examples: <pre><code class='java'>
* // Check that the JSON document does not have an "error" element
* assertThat(response).bodyJson().doesNotHavePath("$.error");
*
* // Check that the response body is strictly equal to the content of "test.json":
* assertThat(response).body().json().isStrictlyEqualToJson("test.json");
* // Check that the JSON document as a top level "message" element
* assertThat(response).bodyJson()
* .extractingPath("$.message").asString().isEqualTo("hello");
* </code></pre>
*/
public ResponseBodyAssert body() {
return new ResponseBodyAssert(getResponse().getContentAsByteArray(),
Charset.forName(getResponse().getCharacterEncoding()), this.jsonMessageConverter);
public AbstractJsonContentAssert<?> bodyJson() {
return new JsonContentAssert(readBody(), this.jsonMessageConverter);
}

private String readBody() {
return new String(getResponse().getContentAsByteArray(),
Charset.forName(getResponse().getCharacterEncoding()));
}

/**
* Return a new {@linkplain AbstractByteArrayAssert assertion} object that
* uses the response body as the object to test.
* @see #bodyText()
* @see #bodyJson()
*/
public AbstractByteArrayAssert<?> body() {
return new ByteArrayAssert(getResponse().getContentAsByteArray());
}

/**
Expand Down Expand Up @@ -89,6 +136,14 @@ public UriAssert redirectedUrl() {
return new UriAssert(getResponse().getRedirectedUrl(), "Redirected URL");
}

/**
* Verify that the response body is equal to the given value.
*/
public SELF hasBodyTextEqualTo(String bodyText) {
bodyText().isEqualTo(bodyText);
return this.myself;
}

/**
* Verify that the forwarded URL is equal to the given value.
* @param forwardedUrl the expected forwarded URL (can be null)
Expand Down

This file was deleted.

0 comments on commit 5567d14

Please sign in to comment.