Add hasJsonPath and doesNotHaveJsonPath asserts

Extend `JsonContentAssert` with `hasJsonPath` and `doesNotHaveJsonPath`
methods which can be used to check the path regardless of the value it
may or may not contain.

Prior to this commit there wasn't an easy way to assert that the
Jackson `@JsonInclude(JsonInclude.Include.NON_NULL)` annotation was
applied since `assertDoesNotHavePathValue` would pass for both
`{"name" : null}` and `{}`.

Closes gh-17608
pull/17614/head
Phillip Webb 5 years ago
parent 900ec9f3ec
commit 0d92af7d55

@ -24,6 +24,7 @@ import java.util.Map;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractBooleanAssert;
import org.assertj.core.api.AbstractCharSequenceAssert;
@ -748,6 +749,21 @@ public class JsonContentAssert extends AbstractAssert<JsonContentAssert, CharSeq
return assertNotPassed(compare(expectedJson, comparator));
}
/**
* Verify that the JSON path is present without checking if it has a value.
* @param expression the {@link JsonPath} expression
* @param args arguments to parameterize the {@code JsonPath} expression with, using
* formatting specifiers defined in {@link String#format(String, Object...)}
* @return {@code this} assertion object
* @throws AssertionError if the value at the given path is missing
* @since 2.2.0
* @see #hasJsonPathValue(CharSequence, Object...)
*/
public JsonContentAssert hasJsonPath(CharSequence expression, Object... args) {
new JsonPathValue(expression, args).assertHasPath();
return this;
}
/**
* Verify that the actual value at the given JSON path produces a non-null result. If
* the JSON path expression is not {@linkplain JsonPath#isDefinite() definite}, this
@ -846,6 +862,21 @@ public class JsonContentAssert extends AbstractAssert<JsonContentAssert, CharSeq
return this;
}
/**
* Verify that the JSON path is not present, even if it has a {@code null} value.
* @param expression the {@link JsonPath} expression
* @param args arguments to parameterize the {@code JsonPath} expression with, using
* formatting specifiers defined in {@link String#format(String, Object...)}
* @return {@code this} assertion object
* @throws AssertionError if the value at the given path is not missing
* @since 2.2.0
* @see #doesNotHaveJsonPathValue(CharSequence, Object...)
*/
public JsonContentAssert doesNotHaveJsonPath(CharSequence expression, Object... args) {
new JsonPathValue(expression, args).assertDoesNotHavePath();
return this;
}
/**
* Verify that the actual value at the given JSON path produces no result. If the JSON
* path expression is not {@linkplain JsonPath#isDefinite() definite}, this method
@ -1049,6 +1080,24 @@ public class JsonContentAssert extends AbstractAssert<JsonContentAssert, CharSeq
}
void assertHasPath() {
try {
read();
}
catch (PathNotFoundException ex) {
failWithMessage("No JSON path \"%s\" found", this.expression);
}
}
void assertDoesNotHavePath() {
try {
read();
failWithMessage("Expecting no JSON path \"%s\"", this.expression);
}
catch (PathNotFoundException ex) {
}
}
void assertHasValue(Class<?> type, String expectedDescription) {
Object value = getValue(true);
if (value == null || isIndefiniteAndEmpty()) {
@ -1080,9 +1129,7 @@ public class JsonContentAssert extends AbstractAssert<JsonContentAssert, CharSeq
Object getValue(boolean required) {
try {
CharSequence json = JsonContentAssert.this.actual;
return this.jsonPath.read((json != null) ? json.toString() : null,
JsonContentAssert.this.configuration);
return read();
}
catch (Exception ex) {
if (required) {
@ -1092,6 +1139,11 @@ public class JsonContentAssert extends AbstractAssert<JsonContentAssert, CharSeq
}
}
private Object read() {
CharSequence json = JsonContentAssert.this.actual;
return this.jsonPath.read((json != null) ? json.toString() : null, JsonContentAssert.this.configuration);
}
private String getNoValueMessage() {
return "No value at JSON path \"" + this.expression + "\"";
}

@ -58,6 +58,8 @@ class JsonContentAssertTests {
private static final String SIMPSONS = loadJson("simpsons.json");
private static final String NULLS = loadJson("nulls.json");
private static JSONComparator COMPARATOR = new DefaultComparator(JSONCompareMode.LENIENT);
@TempDir
@ -839,6 +841,24 @@ class JsonContentAssertTests {
assertThat(forJson(SOURCE)).isNotEqualToJson(createResource(DIFFERENT), COMPARATOR);
}
@Test
void hasJsonPathForPresentAndNotNull() {
assertThat(forJson(NULLS)).hasJsonPath("valuename");
}
@Test
void hasJsonPathForPresentAndNull() {
assertThat(forJson(NULLS)).hasJsonPath("nullname");
}
@Test
void hasJsonPathForNotPresent() {
String expression = "missing";
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(NULLS)).hasJsonPath(expression))
.withMessageContaining("No JSON path \"" + expression + "\" found");
}
@Test
void hasJsonPathValue() {
assertThat(forJson(TYPES)).hasJsonPathValue("$.str");
@ -854,6 +874,22 @@ class JsonContentAssertTests {
assertThat(forJson(TYPES)).hasJsonPathValue("$.emptyMap");
}
@Test
void hasJsonPathValueForANullValue() {
String expression = "nullname";
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(NULLS)).hasJsonPathValue(expression))
.withMessageContaining("No value at JSON path \"" + expression + "\"");
}
@Test
void hasJsonPathValueForMissingValue() {
String expression = "missing";
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(NULLS)).hasJsonPathValue(expression))
.withMessageContaining("No value at JSON path \"" + expression + "\"");
}
@Test
void hasJsonPathValueForIndefinitePathWithResults() {
assertThat(forJson(SIMPSONS)).hasJsonPathValue("$.familyMembers[?(@.name == 'Bart')]");
@ -867,6 +903,27 @@ class JsonContentAssertTests {
.withMessageContaining("No value at JSON path \"" + expression + "\"");
}
@Test
void doesNotHaveJsonPathForMissing() {
assertThat(forJson(NULLS)).doesNotHaveJsonPath("missing");
}
@Test
void doesNotHaveJsonPathForNull() {
String expression = "nullname";
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(NULLS)).doesNotHaveJsonPath(expression))
.withMessageContaining("Expecting no JSON path \"" + expression + "\"");
}
@Test
void doesNotHaveJsonPathForPresent() {
String expression = "valuename";
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(NULLS)).doesNotHaveJsonPath(expression))
.withMessageContaining("Expecting no JSON path \"" + expression + "\"");
}
@Test
void doesNotHaveJsonPathValue() {
assertThat(forJson(TYPES)).doesNotHaveJsonPathValue("$.bogus");
@ -902,6 +959,11 @@ class JsonContentAssertTests {
assertThat(forJson(SIMPSONS)).doesNotHaveJsonPathValue("$.familyMembers[?(@.name == 'Dilbert')]");
}
@Test
void doesNotHaveJsonPathValueForNull() {
assertThat(forJson(NULLS)).doesNotHaveJsonPathValue("nullname");
}
@Test
void hasEmptyJsonPathValueForAnEmptyString() {
assertThat(forJson(TYPES)).hasEmptyJsonPathValue("$.emptyString");

Loading…
Cancel
Save