Assertions for model data. Inspired by JSONAssert and AssertJ. Built on top of Jackson.
Intended as a richer way of writing assertions in unit tests, and as
a more powerful alternative to Spring's jsonPath
.
Describes paths using JSON Pointer syntax, where
a route to the element is a series of /
delimited field names or array indices.
ModelAssert requires Java 8.
Install from Maven Central:
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>model-assert</artifactId>
<version>1.1.0</version>
</dependency>
For a walk-through of key features, there's a tutorial over on Baeldung.com.
String json = "{\"name\":\"ModelAssert\"}";
// assertJ style
assertJson(json)
.at("/name").hasValue("ModelAssert");
// hamcrest style
MatcherAssert.assertThat(json,
json()
.at("/name").hasValue("ModelAssert"));
In the above example, at
is just one of the possible conditions. Here we see Jackson's JSON Pointer syntax in action too.
Semantic comparison of the JSON loaded as both expected and actual.
// assertJ style
assertJson("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}")
.isEqualTo("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}");
// hamcrest style
MatcherAssert.assertThat("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}",
json().isEqualTo("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}"));
These comparisons can be mixed with path asserts, but they compare the whole object structure and report the differences on error, so there's minimum benefit in using both.
By default, the comparison must match everything in order, but the isEqualTo
can be relaxed by using where
:
// allow object keys in any order
assertJson("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}")
.where()
.keysInAnyOrder()
.isEqualTo("{\"versions\":[1.00, 1.01, 1.02], \"name\":\"ModelAssert\"}");
See where context for more examples.
There are more examples in the unit tests, especially ExamplesTest
.
The assertJson
methods produce stand-alone assertions which
execute each clause in order, stopping on error.
The json*
methods - json
, jsonNode
, jsonFile
, jsonFilePath
start the
construction of a hamcrest matcher to which conditions are added.
These are evaluated when the hamcrest matcher's matches
is called.
Note: the DSL is intended to provide auto-complete and is largely fluent. It's also composable, so multiple comparisons can be added after the last one is complete:
assertJson(json)
.at("/name").hasValue("ModelAssert")
.at("/license").hasValue("MIT")
.at("/price").isNull();
If an object can be converted into Jackson's JsonNode
structure, which nearly everything can be,
then it can be compared using ModelAssert:
Map<String, Object> objectMap = new HashMap<>();
objectMap.put("a", UUID.randomUUID().toString());
objectMap.put("b", UUID.randomUUID().toString());
Map<String, Object> expectedMap = new HashMap<>();
expectedMap.put("a", "");
expectedMap.put("b", "");
assertJson(objectMap)
.where()
.path(Pattern.compile("[ab]")).matches(GUID_PATTERN)
.isEqualTo(expectedMap);
As both assertJson
and isEqualTo
allow JsonNode
as an input,
custom conversions to this can be used from any source.
As Jackson can load yaml files, the DSL also supports assertYaml
and isEqualToYaml
/isNotEqualToYaml
:
String yaml1 =
"name: Mr Yaml\n" +
"age: 42\n" +
"items:\n" +
" - a\n" +
" - b\n";
String yaml2 =
"name: Mrs Yaml\n" +
"age: 43\n" +
"items:\n" +
" - c\n" +
" - d\n";
assertYaml(yaml1)
.isNotEqualToYaml(yaml2);
The Hamcrest version of this uses yaml
/yamlFile
and yamlFilePath
:
MatcherAssert.assertThat(yaml1, yaml().isEqualToYaml(yaml2));
The assertion DSL allows a lot of navigation within the json under test. However, it may be desirable to manually load some json for comparison, and perhaps use only a part of that json:
// load some json to compare against
JsonNode jsonNode = JsonProviders.jsonPathProvider().jsonFrom(jsonFile);
// compare "/child" within a source
assertJson(jsonFile)
.at("/child")
// must be equal to the "/child" we've selected
// from an "actual"
.isEqualTo(jsonNode.at("/child"));
The entry point to creating an assertion is:
assertJson
- overloaded to take JSON asString
,JsonNode
,File
orPath
- produces a fluent assertion like AssertJNote: the Jackson parser has been configured to load unquoted field names so:
String unquoted = "{someField: \"value\"}"; // is equivalent to String quoted = "{\"someField\": \"value\"}";
Examples throughout the tests are in the second, more conventional, format.
json
- start creating a hamcrest matcher for aString
jsonNode
- start creating a hamcrest matcher for aJsonNode
jsonFile
- start creating a hamcrest matcher for aFile
jsonFilePath
- start creating a hamcrest matcher for aPath
After that, there are high level methods to add conditions to the matcher:
at
- start creating a JSON Pointer based assertionisNull
/isNotNull
- asserts whether the whole loaded JSON amounts tonull
isEqualTo
/isNotEqualTo
- compare this tree against anothersatisfies
- plug in a customCondition
orConditionList
When a condition has been added to the assertion then the fluent DSL allows for further conditions to be added.
Note: the
assertJson
version executes each condition on the fly, where the hamcrest version stores them for execution until thematches
method is invoked byMatcherAssert.assertThat
or similar.
There are multiple contexts from which assertions are available:
- Assertion - this allows
at
as well as ALL other assertions - Inside
at
- allows anynode
assertion, and then returns toassertion
context - Node - this allows any assertion on the current node, which may be of any valid json type as well as
missing
- Type specific - by calling
number
,text
,object
,array
, orbooleanNode
on a node context DSL, the DSL can be narrowed down to assertions for just that type - this can also be more expressiveit's probably more correct to specify the type and then use assertions relating to that type, but as a shorthand, the assertions are all available without an additional type specifier. Using the type, implicitly creates a check that the node is the correct type, so:assertJson(json) .at("/name").text().isText("My Name");
is the equivalent of:assertJson(json) .at("/name").text().isEmpty();
assertJson(json) .at("/name").isText() .at("/name").isEmpty();
- Where - called before
isEqualTo
to create rules for whole tree comparison
Build a JsonAt
condition by using .at("/some/json/pointer")
.
This is then followed by any of the node context assertions.
Example:
assertJson("{\"name\":null}")
.at("/name").isNull();
The JsonAt
expression is incomplete with just at
, but once the rest of the condition is added,
the this
returned belongs to the main assertion, allowing them to be chained.
assertJson("{\"name\":null}")
.at("/name").isNull()
.at("/address").isMissing();
JSON Pointer expressions treat field names and array indices as /
delimited:
assertJson("{\"names\":[\"Model\",\"Assert\"]}")
.at("/names/1").hasValue("Assert");
These are available on any node of the tree, which might be any type. They include the type specific assertions below, as well as:
hasValue
- assert that a field has a specific valueassertJson(jsonString) .at("/name").hasValue("ModelAssert");
Note: this is very forgiving of type, and may be less precise as a consequence. It detects the expected node type from its input.
isNull
/isNotNull
- assert whether this path resolves tonull
assertJson(jsonString) .at("/price").isNull();
isMissing
/isNotMissing
- assert that this path resolves to missing - i.e. it's an unknown path in the JSONassertJson(jsonString) .at("/random").isMissing();
isAnyNode
- the same asisNotMissing
- useful when used with.where()
in full tree matchingisEmpty
/isNotEmpty
- assert that the json at this location is an empty text, array, or object nodeThis can be combined with a more precise type check and a path in the json:assertJson(someJson) .isEmpty();
or, better still:assertJson(someJson) .at("/name").isText() .at("/name").isEmpty();
Though for brevity, theassertJson(someJson) .at("/name").text().isEmpty();
isEmptyText
/isNotEmptyText
may be easier:assertJson(someJson) .at("/name").isEmptyText();
[!WARNING] Unless you're 100% sure of the type of a node,
isEmpty
andisNotEmpty
are too approximate and should be combined with a DSL-switching type assertion like.text()
,.object()
, or.array()
sinceisEmpty
on aBoolean
makes no sense, and it's not clear whatisNotEmpty
orisEmpty
would mean onnull
.matches(Matcher<JsonNode>)
- assert that the node found at this JSON path matches a hamcrest matcher forJsonNode
This latter example, allows us to reuse the hamcrest form of the json assertion across tests, if there's a common pattern, or allows us to apply a particular set of assertions to only a subtree of the original:assertJson(jsonString) .at("/child/someobject").matches(customHamcrestMatcher);
assertJson(jsonString) .at("/root/child/otherchild/interestingplace") .matches(jsonNode() // jsonNode() creates a new matcher .at("/name").hasValue("Model") .at("/age").hasValue(42));
Note:
satifies
along withConditionList
may be a better solution to subtree assertions withat
assertJson("[" + "{\"name\":\"Model\",\"ok\":true}," + "{\"name\":\"Model\",\"ok\":false}," + "{\"name\":\"Model\"}," + "{\"age\":1234}" + "]") .at("/1").satisfies(conditions() .at("/name").hasValue("Model") .at("/ok").isFalse());
is
/isNot
- provide a description and aPredicate<JsonNode>
to customise with a custom match conditionThis is the unlimited customisable assertion - allowing any test to be done on a per node basis, if it's not already part of the DSL
assertJson("42") .is("Even number", jsonNode -> jsonNode.isNumber() && jsonNode.asInt() % 2 == 0);
is(Function)
- allows customisation with a standard set of match conditions - to modularise the tests:@Test void canApplyStandardSetOfAssertions() { assertJson("{\"root\":{\"name\":\"Mr Name\"}}") .is(ExamplesTest::theUsual) .isNotEmpty(); // additional clause } private static <A> A theUsual(JsonNodeAssertDsl<A> assertion) { return assertion.at("/root/name").isText("Mr Name"); }
isText
/isNotText
- assert that the node is a text node, with optional specific text - note: this can also be achieved withhasValue
, but adds some extra checking that this is a text nodeassertJson("\"theText\"") .isText(); assertJson("\"theText\"") .isText("theText"); assertJson("{\"child\":{\"age\":123}}") .at("/child/age").isNotText(); assertJson("{\"child\":{\"name\":"Bob"}}") .at("/child/age").isNotText("Bert");
isEmptyText
/isNotEmptyText
- both of these require the node to be text, and then assert that the text is""
or notassertJson("\"\"") .isEmptyText(); // FAILS! - wrong type assertJson("0") .isNotEmptyText(); // non empty assertJson("\"0\"") .isNotEmptyText();
matches(Pattern|String)
- assert that the text of this node matches a regular expression - some common patterns are available in thePatterns
classassertJson(jsonString) .at("/guid").matches(GUID_PATTERN);
textMatches
- allows a custom predicate to be passed in order to perform a custom checkassertJson("\"a-b-c\"") .textMatches("Has dashes", text -> text.contains("-"));
textContains
/textDoesNotContain
- reuses the logic of the regular expression matcher to find substringstextStartsWith
/textDoesNotStartWith
- reuses the logic of the regular expression matcher to check the prefix of a text node's text
isGreaterThan
,isGreaterThanOrEqualTo
,isLessThan
,isLessThanOrEqualTo
- these require that the node is a number of a numeric type, and comparesMore specific typed versions -assertJson(jsonString) .at("/count").isGreaterThan(9);
isGreaterThanInt
orisLessThanLong
also exist to avoid a test passing through accidental type coercion or overflow.isBetween
- asserts that a number falls in a rangeassertJson("{number:12}") .at("/number").isBetween(2, 29);
isZero
- asserts that the number is zeroisNumber
,isInteger
,isLong
,isDouble
- assert this is a numeric node or of a specific numeric type
isTrue
/isFalse
- requires the node to be boolean and have the correct valueisBoolean
/isNotBoolean
- asserts the type of the node
isObject
/isNotObject
- asserts the type of the nodecontainsKey
/containsKeys
/doesNotContainKey
/doesNotContainKeys
- checks for the presence of a given set of keys in the objectcontainsKeysExactly
- requires the given keys to be present in the exact order providedcontainsKeysExactlyInAnyOrder
- requires the given keys all to be present, regardless of order in the JSON
isArray
/isNotArray
- asserts the type of the nodeisArrayContaining
/isArrayContainingExactlyInAnyOrder
- potentially slow assertions over the contents of an array. Tries all permutations of matching the provided elements to the array elements, allowing for duplicates. Uses loosehasValue
style matching when values provided:assertJson("[1, 2, 3, 4]") .isArrayContaining(1, 4); assertJson("[1, 2, 3, 4]") .isArrayContainingExactlyInAnyOrder(1, 2, 3, 4);
isArrayContainingExactly
- strictly proves that each element in the array matches the elements provided:This is more efficient at runtime as it has a simple job.assertJson("[1, 2, 3, 4]") .isArrayContainingExactly(1, 2, 3, 4);
There are two main ways to assert the contents of an array. It can be done by value as illustrated above, or it can be done by condition list.
To use the isArrayContaining
suite of functions with a condition list,
we call conditions()
within the ConditionList
class to create a
fluent builder of a list of conditions. As the fluent builder for assertions
adds conditions to the assertion, so the fluent builder inside
ConditionList
treats each additional condition as an element to search for
in the array:
assertJson("[" +
"{\"name\":\"Model\",\"ok\":true}," +
"{\"name\":\"Model\",\"ok\":false}," +
"{\"name\":\"Assert\"}," +
"{\"age\":1234}" +
"]")
.isArrayContainingExactlyInAnyOrder(conditions()
.at("/name").isText("Assert")
.at("/name").hasValue("Model")
.at("/ok").isFalse()
.at("/age").isNumberEqualTo(1234));
In the above example, the conditions, between them, represent a unique
match in each element of the list, but a condition may match more than one
element (as .at("/name".isText("Assert")
does). This is where the
permutational search of the ArrayCondition helps to find the best possible match.
Where a single condition cannot describe the required match for an element
then satisfies
, which is part of every node, allows a ConditionList
:
assertJson("[" +
"{\"name\":\"Model\",\"ok\":true}," +
"{\"name\":\"Model\",\"ok\":false}," +
"{\"name\":\"Model\"}," +
"{\"age\":1234}" +
"]")
.isArrayContainingExactlyInAnyOrder(conditions()
// condition A
.at("/name").isText("Model")
// condition B
.satisfies(conditions()
.at("/name").hasValue("Model")
.at("/ok").isTrue())
// condition C
.satisfies(conditions()
.at("/ok").isFalse()
.at("/name").isText("Model"))
// condition D
.at("/age").isNumberEqualTo(1234));
Each of these composite conditions allows the whole DSL. They're
composed together using Condition.and
.
A Hamcrest matcher could also be used with
ConditionList
viamatches(Matcher<JsonNode>)
Object, String and Array can be said to be sizeable. For Object, the size is the number of keys. For String, it's the number of characters. For Array it's the number of elements.
We can assert this with hasSize
:
assertJson("\"some string\"")
.hasSize(11);
assertJson("[1, 2, 3]")
.hasSize(3);
The general purpose Number
based numeric assertions can be used to assert size via the
size()
function, which enters the NumberComparison
context:
// assert that the array has a size between 3 and 9
assertJson("[1, 2, 3]")
.size().isBetween(3, 9);
The tree comparison is intended to perform a semantic comparison of a JSON tree with another.
It can be used in conjunction with the at
part of the Node DSL:
assertJson("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}")
.at("/versions")
.isEqualTo("[1.00, 1.01, 1.02]");
It can also be customised using where
.
This is used to customise how whole tree comparison works.
The where
function moves us from node context to customisation of isEqualTo
:
assertJson("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}")
.where()
.keysInAnyOrder()
.isEqualTo("{\"versions\":[1.00, 1.01, 1.02], \"name\":\"ModelAssert\"}");
In the where context, we can add general leniency overrides, or specify overrides for particular paths.
keysInAnyOrder
/keysInOrder
- controls whether objects observe order checks - when used just afterwhere
this applies to the whole tree, otherwise it applies to the path exressionobjectContains
- the object ignored missing values in the actualarrayInAnyOrder
- array elements can be in any orderarrayContains
- array elements can be in any order and the actual may have additional elementspath
- start customising the rule for a particular path in the tree:The path is expressed as a series of values, which can be:// turn off key order sensitivity for the `address` field assertJson(...) .where().path("address").keysInAnyOrder() .isEqualTo(...);
String
- conforming to a JSON Pointer, but no/
- Regular expression for matching a field - i.e.
Pattern
PathWildCard
- eitherANY
orANY_SUBTREE
- allowing path matching of one or n levels of fields
at
- a synonym forpath
where the whole JSON Pointer style path is provided - this is a short-hand for paths where there are no wildcards
Within the path expression, we then add further conditions:
- Any conditions from Node context
keysInAnyOrder
/keysInOrder
- specific matches for the current pathobjectContains
- the object ignored missing values in the actualarrayInAnyOrder
- array elements can be in any orderarrayContains
- array elements can be in any order and the actual may have additional elementsisIgnored
- the path is just ignored
The purpose of the where
and path
contexts is to allow for things
which cannot be predicted at the time of coding, or which do not matter
to the result.
A good example is GUIDs in the output. Let's say we have a process which produces JSON with random GUIDs in it. We want to assert that there ARE GUIDs but we can't predict them:
assertJson("{\"a\":{\"guid\":\"fa82142d-13d2-49c4-9878-619c90a9f986\"}," +
"\"b\":{\"guid\":\"96734f31-33c3-4e50-a72b-49bf2d990e33\"}," +
"\"c\":{\"guid\":\"064c8c5a-c9c1-4ea0-bf36-1994104aa870\"}}")
.where()
.path(ANY_SUBTREE, "guid").matches(GUID_PATTERN)
.isEqualTo("{\"a\":{\"guid\":\"?\"}," +
"\"b\":{\"guid\":\"?\"}," +
"\"c\":{\"guid\":\"?\"}}");
Here, the path(ANY_SUBTREE, "guid").matches(GUID_PATTERN)
phrase is
allowing anything ending in guid
to be matched using matches(GUID_PATTERN)
instead of matching it against the JSON inside isEqualTo
.
This can be done more specifically using at
:
assertJson("{\"a\":{\"guid\":\"fa82142d-13d2-49c4-9878-619c90a9f986\"}," +
"\"b\":{\"guid\":\"96734f31-33c3-4e50-a72b-49bf2d990e33\"}," +
"\"c\":{\"guid\":\"064c8c5a-c9c1-4ea0-bf36-1994104aa870\"}}")
.where()
.at("/a/guid").matches(GUID_PATTERN)
.at("/b/guid").matches(GUID_PATTERN)
.at("/c/guid").matches(GUID_PATTERN)
.isEqualTo("{\"a\":{\"guid\":\"?\"}," +
"\"b\":{\"guid\":\"?\"}," +
"\"c\":{\"guid\":\"?\"}}");
Note: the rules used with
where
are evaluated in reverse order so the most general should be provided first, and the most specific last.
Warning: performance implications both arrayInAnyOrder
and arrayContains
try every possible combination of array element in the expected against the
actual in order to work out if the expected elements are present. For
small arrays, this is not a problem, and the unit tests of this project
run very quickly, proving that.
However, an array can, itself, contain objects or other arrays. This can lead to a large permutational explosion, which can take time.
The easiest way to relax array ordering rules is to use where().arrayInAnyOrder()
while setting up isEqualTo
:
assertJson("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}")
.where()
.arrayInAnyOrder()
.isEqualTo("{\"name\":\"ModelAssert\", \"versions\":[1.02, 1.01, 1.00]}");
If only a specific array may be in a random order, it may be better to specialise this by path:
assertJson("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}")
.where()
.path("versions").arrayInAnyOrder()
.isEqualTo("{\"name\":\"ModelAssert\", \"versions\":[1.02, 1.01, 1.00]}");
And, if the value in the expected doesn't contain all the values from the
array in the actual, then we can use arrayContains
to both relax the order
and allow matching of the ones found:
assertJson("{\"name\":\"ModelAssert\",\"versions\":[1.00, 1.01, 1.02]}")
.where()
.path("versions").arrayContains()
.isEqualTo("{\"name\":\"ModelAssert\", \"versions\":[1.02]}");
Note: loose array comparison also honours the rules set in where for the child nodes of the array. The paths described are routes within the actual tree, not the expected tree.. So as every combination of match is tried, the path rules may perform different comparisons on the expected data, as it's checked against each actual.
The configuredBy
function on the WhereDsl
allows a common comparison configuration
to be implemented and plugged in:
@Test
void matchesAnyGuidUsingCommonConfiguration() {
assertJson("{\"a\":{\"guid\":\"fa82142d-13d2-49c4-9878-619c90a9f986\"}," +
"\"b\":{\"guid\":\"96734f31-33c3-4e50-a72b-49bf2d990e33\"}," +
"\"c\":{\"guid\":\"064c8c5a-c9c1-4ea0-bf36-1994104aa870\"}}")
.where()
.configuredBy(ExamplesTest::ignoreGuids)
.isEqualTo("{\"a\":{\"guid\":\"?\"}," +
"\"b\":{\"guid\":\"?\"}," +
"\"c\":{\"guid\":\"?\"}}");
}
private static <A> WhereDsl<A> ignoreGuids(WhereDsl<A> where) {
return where.path(ANY_SUBTREE, "guid").matches(GUID_PATTERN);
}
There's room for custom assertions throughout the DSL, and if necessary,
the Satisfies
interface, allows a condition to be added fluently. Conditions
are based on the Condition
class. The existing conditions can be used directly
if necessary, and can be composed using Condition.and
or Condition.or
where needed. Similarly, there's a not
method in the Condition
class Not
to invert any condition as well as invert
on Condition
to invert
the current condition.
A custom condition can be fed to satisfies
:
// using `and` along with functions from the
// condition classes
assertJson("\"some string\"").satisfies(
textMatches(Pattern.compile("[a-z ]+"))
.and(new HasSize(12)));
// using or and inverting the condition - this will
// pass as it fails both the ORed conditions, but the
// whole statement is inverted
assertJson("\"some string!!!\"").satisfies(
textMatches(Pattern.compile("[a-z ]+"))
.or(new HasSize(12))
.inverted());
The assertions can be used stand-alone with assertJson
or can be built as Hamcrest matchers. The assertion
can also be converted to a Mockito
ArgumentMatcher
.
Assuming Mockito 3, the toArgumentMatcher
method converts the Hamcrest
style syntax into Mockito's native
ArgumentMatcher
. Older versions of Mockito
used Hamcrest natively.
The json matcher can then be used to detect calls to a function either with verify
/then
or when setting
up responses to different inputs:
// detecting calls based on the json values passed
someInterface.findValueFromJson("{\"name\":\"foo\"}");
then(someInterface)
.should()
.findValueFromJson(argThat(json()
.at("/name").hasValue("foo")
.toArgumentMatcher()));
// setting up responses based on the json
given(someInterface.findValueFromJson(argThat(json()
.at("/name").hasValue("foo")
.toArgumentMatcher())))
.willReturn("foo");
assertThat(someInterface.findValueFromJson("{\"name\":\"foo\"}")).isEqualTo("foo");
Note, this works with all the types of JSON input sources supported by the Hamcrest version of the library.
You need to choose the type of input via the json
, jsonFile
methods etc.
Rather than:
// clause inside ResultMatcher
jsonPath("$.name", "ModelAssert")
We can construct the hamcrest matcher version of ModelAssert's JsonAssertion:
content().string(
json()
.at("/name")
.hasValue("ModelAssert"))
While this syntax is of limited value in this simple case, the more powerful comparisons supported
by this library are equally possible after the json()
statement starts creating a matcher.
By default, Model Assert uses two ObjectMapper
objects - one for loading JSON and one for loading YAML.
These can be overridden for the current thread (allowing concurrent testing) and it's advisable to do this
in the @BeforeAll
of a text fixture:
@BeforeAll
static void beforeAll() {
// support LocalDateTime
overrideObjectMapper(defaultObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
// stop parsing `yes` to mean Boolean `true`
overrideYamlObjectMapper(new ObjectMapper(
new YAMLFactory()
.configure(PARSE_BOOLEAN_LIKE_WORDS_AS_STRINGS, true)));
}
and when replacing the object mapper in setup, it's a good idea to put it back in the tear down:
@AfterAll
static void afterAll() {
clearObjectMapperOverride();
clearYamlObjectMapperOverride();
}
Any assertions used while the override is in place will use the alternative object mapper.
Note: if using a common alternative object mapper, maybe consider building a small JUnit 5 test extension or use a base class for your tests which contains the common set up
The functions defaultObjectMapper
and defaultYamlMapper
in JsonProviders
can be used to create a basic ObjectMapper
to base a custom one on.
The classes in the root package uk.org.webcompere.modelassert.json
are the jumping
on point for the API and they will be changed rarely.
Functions elsewhere will be accessed via the fluent API and may move between packages in later versions, though this should be resolved without changing consuming code.
SemVer numbering will indicate possible breaking changes by increments to the minor version number. Patch versions are unlikely to have any noticeable effect on the API.
If you experience any problems using this library, or have any ideas, then please raise an issue. Please check for any existing issues first.
PRs will be accepted if they come with unit tests and are linked to an issue.