net.javacrumbs.jsonunit.assertj.JsonAssert Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of json-unit-assertj Show documentation
Show all versions of json-unit-assertj Show documentation
JSON Assertions based on AssertJ
The newest version!
/**
* Copyright 2009-2019 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.javacrumbs.jsonunit.assertj;
import static net.javacrumbs.jsonunit.core.internal.Diff.quoteTextValue;
import static net.javacrumbs.jsonunit.core.internal.JsonUtils.getNode;
import static net.javacrumbs.jsonunit.core.internal.JsonUtils.getPathPrefix;
import static net.javacrumbs.jsonunit.core.internal.Node.NodeType.ARRAY;
import static net.javacrumbs.jsonunit.core.internal.Node.NodeType.BOOLEAN;
import static net.javacrumbs.jsonunit.core.internal.Node.NodeType.NULL;
import static net.javacrumbs.jsonunit.core.internal.Node.NodeType.NUMBER;
import static net.javacrumbs.jsonunit.core.internal.Node.NodeType.OBJECT;
import static net.javacrumbs.jsonunit.core.internal.Node.NodeType.STRING;
import static net.javacrumbs.jsonunit.jsonpath.InternalJsonPathUtils.resolveJsonPaths;
import static org.assertj.core.description.Description.mostRelevantDescription;
import static org.assertj.core.util.Strings.isNullOrEmpty;
import java.math.BigDecimal;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import net.javacrumbs.jsonunit.core.Configuration;
import net.javacrumbs.jsonunit.core.ConfigurationWhen.ApplicableForPath;
import net.javacrumbs.jsonunit.core.ConfigurationWhen.PathsParam;
import net.javacrumbs.jsonunit.core.Option;
import net.javacrumbs.jsonunit.core.internal.Diff;
import net.javacrumbs.jsonunit.core.internal.JsonUtils;
import net.javacrumbs.jsonunit.core.internal.Node;
import net.javacrumbs.jsonunit.core.internal.Path;
import net.javacrumbs.jsonunit.core.internal.matchers.InternalMatcher;
import net.javacrumbs.jsonunit.core.listener.DifferenceListener;
import net.javacrumbs.jsonunit.jsonpath.JsonPathAdapter;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractStringAssert;
import org.assertj.core.api.BigDecimalAssert;
import org.assertj.core.api.BigIntegerAssert;
import org.assertj.core.api.BooleanAssert;
import org.assertj.core.api.InstanceOfAssertFactory;
import org.assertj.core.api.StringAssert;
import org.assertj.core.api.UriAssert;
import org.assertj.core.description.Description;
import org.assertj.core.error.MessageFormatter;
import org.hamcrest.Matcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class JsonAssert extends AbstractAssert {
final Path path;
final Configuration configuration;
private final Object actualForMatcher;
JsonAssert(Path path, Configuration configuration, Object actual, boolean alreadyParsed) {
super(
alreadyParsed ? JsonUtils.wrapDeserializedObject(actual) : JsonUtils.convertToJson(actual, "actual"),
JsonAssert.class);
this.path = path;
this.configuration = configuration;
this.actualForMatcher = alreadyParsed ? JsonUtils.wrapDeserializedObject(actual) : actual;
usingComparator(new JsonComparator(configuration, path, false));
}
JsonAssert(Path path, Configuration configuration, Object actual) {
this(path, configuration, actual, false);
}
JsonAssert(Object actual, Configuration configuration) {
this(Path.create("", getPathPrefix(actual)), configuration, actual);
}
/**
* Moves comparison to given node. Second call navigates from the last position in the JSON.
*/
@NotNull
public JsonAssert node(@NotNull String node) {
return new JsonAssert(path.to(node), configuration, getNode(actual, node));
}
/**
* Allows to do multiple comparisons on a document like
*
*
* assertThatJson("{\"test\":{\"a\":1, \"b\":2, \"c\":3}}").and(
* a -> a.node("test").isObject(),
* a -> a.node("test.b").isEqualTo(3)
* );
*
*/
@NotNull
public JsonAssert and(@NotNull JsonAssertion... assertions) {
Arrays.stream(assertions).forEach(a -> a.doAssert(this));
return this;
}
/**
* Compares JSONs.
*/
@Override
@NotNull
public JsonAssert isEqualTo(@Nullable Object expected) {
Diff diff = Diff.create(expected, actual, "fullJson", path.asPrefix(), configuration);
String overridingErrorMessage = info.overridingErrorMessage();
if (!isNullOrEmpty(overridingErrorMessage) && !diff.similar()) {
failWithMessage(overridingErrorMessage);
} else {
diff.failIfDifferent(MessageFormatter.instance().format(info.description(), info.representation(), ""));
}
return this;
}
/**
* Asserts that given node is present and is of type object.
*
* @return MapAssert where the object is serialized as Map
*/
@NotNull
@SuppressWarnings("unchecked")
public JsonMapAssert isObject() {
Node node = assertType(OBJECT);
return describe(new JsonMapAssert((Map) node.getValue(), path.asPrefix(), configuration));
}
/**
* Asserts that given node is present and is of type number.
*/
@NotNull
public BigDecimalAssert isNumber() {
Node node = assertType(NUMBER);
return createBigDecimalAssert(node.decimalValue());
}
/**
* Asserts that the value is an integer. 1 is an integer 1.0, 1.1, 1e3, 1e0, 1e-3 is not.
*/
public BigIntegerAssert isIntegralNumber() {
Node node = internalMatcher().assertIntegralNumber();
return describe(new BigIntegerAssert(node.decimalValue().toBigIntegerExact()));
}
/**
* Asserts that given node is present and is of type number or a string that can be parsed as a number.
*/
@NotNull
public BigDecimalAssert asNumber() {
internalMatcher().isPresent(NUMBER.getDescription());
Node node = getNode(actual, "");
if (node.getNodeType() == NUMBER) {
return createBigDecimalAssert(node.decimalValue());
} else if (node.getNodeType() == STRING) {
try {
return createBigDecimalAssert(new BigDecimal(node.asText()));
} catch (NumberFormatException e) {
failWithMessage("Node \"" + path + "\" can not be converted to number expected: but was: <"
+ quoteTextValue(node.getValue()) + ">.");
}
} else {
internalMatcher().failOnType(node, "number or string");
}
//noinspection DataFlowIssue
return null;
}
private BigDecimalAssert createBigDecimalAssert(BigDecimal value) {
return describe(new BigDecimalAssert(value));
}
private InternalMatcher internalMatcher() {
String description = mostRelevantDescription(info.description(), "Node \"" + path + "\"");
return new InternalMatcher(actualForMatcher, path.asPrefix(), "", configuration, description);
}
/**
* Asserts that given node is present and is of type array.
*/
@NotNull
public JsonListAssert isArray() {
Node node = assertType(ARRAY);
return createListAssert(node).as("Node \"%s\"", path);
}
private @NotNull JsonListAssert createListAssert(Node node) {
return new JsonListAssert((List>) node.getValue(), path.asPrefix(), configuration);
}
/**
* Asserts that given node is present and is of type boolean.
*/
@NotNull
public BooleanAssert isBoolean() {
Node node = assertType(BOOLEAN);
return createBooleanAssert(node);
}
private BooleanAssert createBooleanAssert(Node node) {
return describe(new BooleanAssert((Boolean) node.getValue()));
}
/**
* Asserts that given node is present and is of type string.
*/
@NotNull
public StringAssert isString() {
Node node = assertType(STRING);
return createStringAssert(node);
}
private StringAssert createStringAssert(Node node) {
return describe(new StringAssert((String) node.getValue()));
}
private > T describe(T ass) {
return ass.as("Different value found in node \"%s\"", path);
}
@Override
@NotNull
public AbstractStringAssert> asString() {
return isString();
}
/**
* Asserts that given node is present and is null.
*/
@Override
public void isNull() {
assertType(NULL);
}
/**
* Asserts that given node is present and is URI.
*/
@NotNull
public UriAssert isUri() {
Node node = assertType(STRING);
return describe(new UriAssert(URI.create((String) node.getValue())));
}
/**
* Asserts that given node is present.
*/
@NotNull
public JsonAssert isPresent() {
internalMatcher().isPresent();
return this;
}
/**
* Asserts that given node is absent.
*/
public void isAbsent() {
internalMatcher().isAbsent();
}
/**
* Asserts that given node is present and is not null.
*/
@Override
@NotNull
public JsonAssert isNotNull() {
internalMatcher().isNotNull();
return this;
}
private Node assertType(Node.NodeType type) {
return internalMatcher().assertType(type);
}
@Override
public > ASSERT asInstanceOf(
InstanceOfAssertFactory, ASSERT> instanceOfAssertFactory) {
Node node = internalMatcher().getActualNode();
var ass =
switch (node.getNodeType()) {
case OBJECT -> throw new UnsupportedOperationException(
"asInstanceOf is not supported for Object type");
case ARRAY -> createListAssert(node);
case STRING -> createStringAssert(node);
case NUMBER -> createBigDecimalAssert(node.decimalValue());
case BOOLEAN -> createBooleanAssert(node);
case NULL -> new StringAssert(null);
};
return ass.asInstanceOf(instanceOfAssertFactory);
}
/**
* JsonAssert that can be configured to prevent mistakes like
*
*
* assertThatJson(...).isEqualsTo(...).when(...);
*
*/
public static class ConfigurableJsonAssert extends JsonAssert {
// Want to pass to inPath to not parse twice.
private final Object originalActual;
ConfigurableJsonAssert(Path path, Configuration configuration, Object actual) {
super(path, configuration, actual);
this.originalActual = actual;
}
ConfigurableJsonAssert(Object actual, Configuration configuration) {
this(Path.create("", getPathPrefix(actual)), configuration, actual);
}
/**
* Adds comparison options.
*/
@NotNull
public ConfigurableJsonAssert when(@NotNull Option first, @NotNull Option... other) {
return withConfiguration(c -> c.when(first, other));
}
/**
* Adds path specific options.
*
* @see Configuration#when(PathsParam, ApplicableForPath...)
*/
@NotNull
public final ConfigurableJsonAssert when(@NotNull PathsParam object, @NotNull ApplicableForPath... actions) {
return withConfiguration(c -> c.when(object, actions));
}
/**
* Adds comparison options.
*/
@NotNull
public ConfigurableJsonAssert withOptions(@NotNull Option first, @NotNull Option... next) {
return withConfiguration(c -> c.withOptions(first, next));
}
/**
* Adds comparison options.
*/
@NotNull
public ConfigurableJsonAssert withOptions(@NotNull Collection