Expand description
Getting Started | Examples | Values | Objects | Arrays | Combined example
A set of matcher macros for ergonomic JSON testing with googletest-rust.
These tiny, focused matchers make it effortless to assert on serde_json::Value
in Rust tests.
§Features
- Value: Match JSON primitive values (
string
,number
(i64/f64),bool
) using thejson::value!
macro, and matchnull
values withjson::is_null()
. - Object: Pattern-match JSON objects by fields using the
json::matches_pattern!{...}
macro; supports both * strict* mode ( all fields must match and no extra fields) and non-strict mode (extra fields allowed via..
). - Array: Match arrays element-by-element (ordered, supports heterogeneous types) using the
json::elements_are![...]
macro. - Unordered array: Match arrays where order does not matter using the
json::unordered_elements_are![...]
macro. - Contains-each (arrays): Require that each expected matcher matches a unique element in any order, allowing extra
elements, with the
json::contains_each![...]
macro. - Containment (arrays): Assert that an array is a subset of the expected matchers (i.e., every actual element is
accounted for) using the
json::is_contained_in![...]
macro. - Clear, structured diagnostic failure messages: When a match fails, detailed explanations show which part of the JSON structure did not match and why.
§Getting Started
Add to your Cargo.toml
:
[dev-dependencies]
googletest = "0.14"
serde_json = "1"
googletest-json-serde = "0.1" # replace with the latest version on crates.io
This crate is typically only needed as a dev-dependency.
In tests:
use googletest::prelude::*;
use googletest_json_serde::json;
use serde_json::json as j;
The crate re-exports a
json
namespace with everything you need:
json::value!(...)
– match a value inside a JSONValue
json::matches_pattern!
– explicitly match JSON objects by fields (withjson::pat!
as an alias)json::elements_are![...]
– match arrays element-by-element (ordered)json::unordered_elements_are![...]
– match arrays element-by-element (unordered)json::is_contained_in![...]
– assert containment of JSON elements
§Examples
§Values
§Match JSON values with json::value
fn values() {
assert_that!(j!(42), json::value!(gt(40i64)));
assert_that!(j!(3.14), json::value!(near(3.1f64, 0.1f64)));
assert_that!(j!("hello"), json::value!(starts_with("he")));
assert_that!(j!(true), json::value!(is_true()));
assert_that!(j!(null), json::is_null());
}
§Objects
§Match JSON objects with json::matches_pattern!
Strict match:
fn object_strict() {
let v = j!({"name": "Alice", "age": 30.0});
assert_that!(
v,
json::matches_pattern!({
"name": eq("Alice"),
"age": eq(30.0),
})
);
}
Non-strict match (with ..
):
fn object_non_strict() {
let v = j!({"name": "Alice", "age": 30.0, "extra": "ignored"});
assert_that!(
v,
json::matches_pattern!({
"name": eq("Alice"),
..
})
);
}
§Arrays
§Match arrays with json::elements_are!
(ordered)
fn arrays_ordered() {
assert_that!(
j!(["hello", 42, true]),
json::elements_are![eq("hello"), eq(42), eq(true)]
);
assert_that!(
j!(["hello", 42, true]),
not(json::elements_are![eq("hello"), eq(42), eq(false)])
);
}
§Match arrays with json::unordered_elements_are!
(unordered)
fn arrays_unordered() {
assert_that!(
j!([42, "hello", true]),
json::unordered_elements_are![eq(42), eq(true), eq("hello")]
);
assert_that!(
j!([42, "hello", true]),
not(json::unordered_elements_are![eq(42), eq(false), eq("hello")])
);
}
§Assert containment with json::is_contained_in!
fn containment() {
assert_that!(
j!(["a", "b", "c"]),
json::is_contained_in![eq("a"), eq("c")]
);
assert_that!(
j!(["a", "b", "c"]),
not(json::is_contained_in![eq("a"), eq("d")])
);
}
§Assert each matcher finds a unique element with json::contains_each!
fn contains_each() {
// Array can have extra elements, but must contain all required ones
assert_that!(
j!(["admin", "user", "tester", "viewer"]),
json::contains_each![eq("admin"), eq("tester")]
);
assert_that!(
j!(["admin", "user", "tester", "viewer"]),
not(json::contains_each![eq("admin"), eq("missing")])
);
}
§Array Matcher Quick Reference
Here’s a quick reference matrix comparing the array matchers:
Matcher | Order Matters | Extra Elements OK | Missing Elements OK | Use Case |
---|---|---|---|---|
elements_are! | Yes | No | No | Exact ordered match of all elements |
unordered_elements_are! | No | No | No | Exact unordered match of all elements |
contains_each! | No | Yes | No | Require each matcher to match a unique element, extra allowed |
is_contained_in! | No | No | Yes | Actual elements are subset of expected |
§Combined example
Compose all together for complex structures. Here is a Rust struct with a JSON field, using matches_pattern!
for the
struct and nested JSON matchers for the field:
#[derive(Debug)]
struct Response {
status: u32,
payload: serde_json::Value,
}
fn combined_match() {
let resp = Response {
status: 200,
payload: j!({
"user": {
"id": 123,
"name": "Alice",
"roles": ["admin", "user", "tester"],
"permissions": ["read", "write", "delete", "extra_perm"],
"settings": {
"theme": "dark",
"notifications": true,
"beta_features": null
},
"login_times": [1640995200, 1641081600, 1641168000],
"metadata": {"created": "2021-01-01", "source": "api"}
}
}),
};
assert_that!(
resp,
matches_pattern!(Response {
status: eq(&200),
payload: json::matches_pattern!({
"user": json::matches_pattern!({
// Value matchers
"id": json::value!(gt(100i64)),
"name": json::value!(starts_with("Ali")),
// Ordered array matching
"roles": json::elements_are![eq("admin"), eq("user"), eq("tester")],
// Unordered array matching (subset with unique matches)
"permissions": json::contains_each![eq("read"), eq("write")],
// Nested object with null value
"settings": json::matches_pattern!({
"theme": json::value!(eq("dark")),
"notifications": json::value!(is_true()),
"beta_features": json::is_null(),
}),
// Unordered array matching (exact)
"login_times": json::unordered_elements_are![
eq(1641168000), eq(1640995200), eq(1641081600)
],
// Non-strict object matching (allows extra fields)
"metadata": json::matches_pattern!({
"created": json::value!(starts_with("2021")),
..
}),
// Allow extra fields in user object
..
})
})
})
);
}
§Acknowledgements
Parts of this crate are adapted from googletest-rust, which is licensed under Apache 2.0.