serde_json_assert/
lib.rs

1//! This crate includes macros for comparing two serializable values by diffing their JSON
2//! representations. It is designed to give much more helpful error messages than the standard
3//! [`assert_eq!`]. It basically does a diff of the two objects and tells you the exact
4//! differences. This is useful when asserting that two large JSON objects are the same.
5//!
6//! It uses the [serde] and [serde_json] to perform the serialization.
7//!
8//! [serde]: https://crates.io/crates/serde
9//! [serde_json]: https://crates.io/crates/serde_json
10//! [`assert_eq!`]: https://doc.rust-lang.org/std/macro.assert_eq.html
11//!
12//! ## Partial matching
13//!
14//! If you want to assert that one JSON value is "included" in another use
15//! [`assert_json_include`](macro.assert_json_include.html):
16//!
17//! ```should_panic
18//! use serde_json_assert::assert_json_include;
19//! use serde_json::json;
20//!
21//! let a = json!({
22//!     "data": {
23//!         "users": [
24//!             {
25//!                 "id": 1,
26//!                 "country": {
27//!                     "name": "Denmark"
28//!                 }
29//!             },
30//!             {
31//!                 "id": 24,
32//!                 "country": {
33//!                     "name": "Denmark"
34//!                 }
35//!             }
36//!         ]
37//!     }
38//! });
39//!
40//! let b = json!({
41//!     "data": {
42//!         "users": [
43//!             {
44//!                 "id": 1,
45//!                 "country": {
46//!                     "name": "Sweden"
47//!                 }
48//!             },
49//!             {
50//!                 "id": 2,
51//!                 "country": {
52//!                     "name": "Denmark"
53//!                 }
54//!             }
55//!         ]
56//!     }
57//! });
58//!
59//! assert_json_include!(actual: a, expected: b)
60//! ```
61//!
62//! This will panic with the error message:
63//!
64//! ```text
65//! json atoms at path ".data.users[0].country.name" are not equal:
66//!     expected:
67//!         "Sweden"
68//!     actual:
69//!         "Denmark"
70//!
71//! json atoms at path ".data.users[1].id" are not equal:
72//!     expected:
73//!         2
74//!     actual:
75//!         24
76//! ```
77//!
78//! [`assert_json_include`](macro.assert_json_include.html) allows extra data in `actual` but not in
79//! `expected`. That is so you can verify just a part of the JSON without having to specify the
80//! whole thing. For example this test passes:
81//!
82//! ```
83//! use serde_json_assert::assert_json_include;
84//! use serde_json::json;
85//!
86//! assert_json_include!(
87//!     actual: json!({
88//!         "a": { "b": 1 },
89//!     }),
90//!     expected: json!({
91//!         "a": {},
92//!     })
93//! )
94//! ```
95//!
96//! However `expected` cannot contain additional data so this test fails:
97//!
98//! ```should_panic
99//! use serde_json_assert::assert_json_include;
100//! use serde_json::json;
101//!
102//! assert_json_include!(
103//!     actual: json!({
104//!         "a": {},
105//!     }),
106//!     expected: json!({
107//!         "a": { "b": 1 },
108//!     })
109//! )
110//! ```
111//!
112//! That will print
113//!
114//! ```text
115//! json atom at path ".a.b" is missing from actual
116//! ```
117//!
118//! ## Exact matching
119//!
120//! If you want to ensure two JSON values are *exactly* the same, use
121//! [`assert_json_eq`](macro.assert_json_eq.html).
122//!
123//! ```rust,should_panic
124//! use serde_json_assert::assert_json_eq;
125//! use serde_json::json;
126//!
127//! assert_json_eq!(
128//!     json!({ "a": { "b": 1 } }),
129//!     json!({ "a": {} })
130//! )
131//! ```
132//!
133//! This will panic with the error message:
134//!
135//! ```text
136//! json atom at path ".a.b" is missing from lhs
137//! ```
138//! ## Custom message
139//!
140//! Like [`assert!`], the `assert_json` macros all have second forms, where a
141//! custom panic message can be provided. These messages are printed in
142//! addition to the default messages.
143//!
144//! ```rust
145//! use serde_json_assert::assert_json_eq;
146//! use serde_json::json;
147//!
148//! let lhs = json!({ "a": 1 });
149//! let rhs = json!({ "a": 1 });
150//!
151//! assert_json_eq!(
152//!     lhs,
153//!     rhs,
154//!     "We are testing {} and {} for equality",
155//!     "A",
156//!     "B"
157//! )
158//! ```
159//!
160//! ## Further customization
161//!
162//! You can use [`assert_json_matches`] to further customize the comparison.
163
164#![deny(
165    missing_docs,
166    unused_imports,
167    missing_debug_implementations,
168    missing_copy_implementations,
169    trivial_casts,
170    trivial_numeric_casts,
171    unsafe_code,
172    unstable_features,
173    unused_import_braces,
174    unused_qualifications,
175    unknown_lints
176)]
177
178use diff::diff;
179use serde::Serialize;
180
181pub use crate::diff::{Difference, Key, Path};
182
183mod core_ext;
184mod diff;
185
186/// Assert that a JSON value contains other JSON value
187///
188/// See [crate documentation](index.html) for examples.
189#[macro_export]
190macro_rules! assert_json_contains {
191    (container: $container:expr, contained: $contained:expr $(,)?) => {{
192        let config =
193            $crate::Config::new($crate::CompareMode::Inclusive).consider_array_sorting(false);
194        $crate::assert_json_matches!($container, $contained, &config)
195    }};
196    (container: $container:expr, contained: $contained:expr, $($arg:tt)+) => {{
197        let config =
198            $crate::Config::new($crate::CompareMode::Inclusive).consider_array_sorting(false);
199        $crate::assert_json_matches!($container, $contained, &config, $($arg)+)
200    }};
201}
202
203/// Compare two JSON values for an inclusive match.
204///
205/// It allows `actual` to contain additional data. If you want an exact match use
206/// [`assert_json_eq`](macro.assert_json_eq.html) instead.
207///
208/// See [crate documentation](index.html) for examples.
209#[macro_export]
210macro_rules! assert_json_include {
211    (actual: $actual:expr, expected: $expected:expr $(,)?) => {{
212        let config = $crate::Config::new($crate::CompareMode::Inclusive);
213        $crate::assert_json_matches!($actual, $expected, &config)
214    }};
215    (expected: $expected:expr, actual: $actual:expr $(,)?) => {{
216        $crate::assert_json_include!(actual: $actual, expected: $expected)
217    }};
218    (actual: $actual:expr, expected: $expected:expr, $($arg:tt)+) => {{
219        let config = $crate::Config::new($crate::CompareMode::Inclusive);
220        $crate::assert_json_matches!($actual, $expected, &config, $($arg)+)
221    }};
222    (expected: $expected:expr, actual: $actual:expr, $($arg:tt)+) => {{
223        $crate::assert_json_include!(actual: $actual, expected: $expected, $($arg)+)
224    }};
225}
226
227/// Compare two JSON values for an exact match.
228///
229/// If you want an inclusive match use [`assert_json_include`](macro.assert_json_include.html)
230/// instead.
231///
232/// See [crate documentation](index.html) for examples.
233#[macro_export]
234macro_rules! assert_json_eq {
235    ($lhs:expr, $rhs:expr $(,)?) => {{
236        let config = $crate::Config::new($crate::CompareMode::Strict);
237        $crate::assert_json_matches!($lhs, $rhs, &config)
238    }};
239    ($lhs:expr, $rhs:expr, $($arg:tt)+) => {{
240        let config = $crate::Config::new($crate::CompareMode::Strict);
241        $crate::assert_json_matches!($lhs, $rhs, &config, $($arg)+)
242    }};
243}
244
245/// Compare two JSON values according to a configuration.
246///
247/// ```
248/// use serde_json_assert::{
249///     CompareMode,
250///     Config,
251///     NumericMode,
252///     assert_json_matches,
253/// };
254/// use serde_json::json;
255///
256/// let config = Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat);
257///
258/// assert_json_matches!(
259///     json!({
260///         "a": { "b": [1, 2, 3.0] },
261///     }),
262///     json!({
263///         "a": { "b": [1, 2.0, 3] },
264///     }),
265///     &config,
266/// );
267///
268/// assert_json_matches!(
269///     json!({
270///         "a": { "b": [1, 2, 3.0] },
271///     }),
272///     json!({
273///         "a": { "b": [1, 2.0, 3] },
274///     }),
275///     &config,
276///     "Failed to assert equality between {} and {}",
277///     "lhs",
278///     "rhs"
279/// );
280/// ```
281///
282/// When using `CompareMode::Inclusive` the first argument is `actual` and the second argument is
283/// `expected`. Example:
284///
285/// ```
286/// # use serde_json_assert::{
287/// #     CompareMode,
288/// #     Config,
289/// #     NumericMode,
290/// #     assert_json_matches,
291/// #     assert_json_include,
292/// # };
293/// # use serde_json::json;
294/// #
295/// // This
296/// let config = Config::new(CompareMode::Inclusive);
297/// assert_json_matches!(
298///     json!({
299///         "a": { "b": 1 },
300///     }),
301///     json!({
302///         "a": {},
303///     }),
304///     &config,
305/// );
306///
307/// // Is the same as this
308/// assert_json_include!(
309///     actual: json!({
310///         "a": { "b": 1 },
311///     }),
312///     expected: json!({
313///         "a": {},
314///     }),
315/// );
316/// ```
317#[macro_export]
318macro_rules! assert_json_matches {
319    ($lhs:expr, $rhs:expr, $config:expr $(,)?) => {{
320        if let Err(error) = $crate::assert_json_matches_no_panic(&$lhs, &$rhs, $config) {
321            panic!("\n{}", error);
322        }
323    }};
324    ($lhs:expr, $rhs:expr, $config:expr, $($arg:tt)+) => {{
325        if let Err(error) = $crate::assert_json_matches_no_panic(&$lhs, &$rhs, $config) {
326            panic!("\n{}\n\n{}", format_args!($($arg)+), error);
327        }
328    }};
329}
330
331/// Compares two JSON values without panicking.
332///
333/// Instead it returns a `Result` where the error is the message that would be passed to `panic!`.
334/// This is might be useful if you want to control how failures are reported and don't want to deal
335/// with panics.
336pub fn assert_json_matches_no_panic<Lhs, Rhs>(
337    lhs: &Lhs,
338    rhs: &Rhs,
339    config: &Config,
340) -> Result<(), String>
341where
342    Lhs: Serialize,
343    Rhs: Serialize,
344{
345    let lhs = serde_json::to_value(lhs).unwrap_or_else(|err| {
346        panic!(
347            "Couldn't convert left hand side value to JSON. Serde error: {}",
348            err
349        )
350    });
351    let rhs = serde_json::to_value(rhs).unwrap_or_else(|err| {
352        panic!(
353            "Couldn't convert right hand side value to JSON. Serde error: {}",
354            err
355        )
356    });
357
358    let diffs = diff(&lhs, &rhs, config);
359
360    if diffs.is_empty() {
361        Ok(())
362    } else {
363        let msg = diffs
364            .into_iter()
365            .map(|d| d.to_string())
366            .collect::<Vec<_>>()
367            .join("\n\n");
368        Err(msg)
369    }
370}
371
372/// Compares two JSON values without panicking.
373///
374/// Returns a `Result` containing either `Ok(())` if the values match,
375/// or an `Err` with a [`Vec<Difference>`](Difference) describing the differences.
376///
377/// # Note:
378///
379/// This function performs some cloning and may be less efficient.
380///
381/// If you only need a string error message, use [`assert_json_matches_no_panic`] or the assertion
382/// macros.
383///
384/// # Examples
385///
386/// ```
387/// use serde_json_assert::{try_assert_json_matches, Config, CompareMode};
388/// use serde_json::json;
389///
390/// let lhs = json!({ "a": 1, "b": 2 });
391/// let rhs = json!({ "a": 1 });
392/// let config = Config::new(CompareMode::Inclusive);
393///
394/// let result = try_assert_json_matches(&lhs, &rhs, &config);
395/// assert!(result.is_ok());
396///
397/// let lhs = json!({ "a": 1 });
398/// let rhs = json!({ "a": 2 });
399/// let config = Config::new(CompareMode::Strict);
400///
401/// let result = try_assert_json_matches(&lhs, &rhs, &config);
402/// assert!(result.is_err());
403/// ```
404pub fn try_assert_json_matches<Lhs, Rhs>(
405    lhs: &Lhs,
406    rhs: &Rhs,
407    config: &Config,
408) -> Result<(), Vec<Difference>>
409where
410    Lhs: Serialize,
411    Rhs: Serialize,
412{
413    let lhs = serde_json::to_value(lhs).unwrap_or_else(|err| {
414        panic!(
415            "Couldn't convert left hand side value to JSON. Serde error: {}",
416            err
417        )
418    });
419    let rhs = serde_json::to_value(rhs).unwrap_or_else(|err| {
420        panic!(
421            "Couldn't convert right hand side value to JSON. Serde error: {}",
422            err
423        )
424    });
425
426    let diffs = diff(&lhs, &rhs, config);
427    let diffs_buf: Vec<Difference> = diffs.into_iter().map(|d| d.into()).collect();
428
429    if diffs_buf.is_empty() {
430        Ok(())
431    } else {
432        Err(diffs_buf)
433    }
434}
435
436/// Configuration for how JSON values should be compared.
437#[derive(Debug, Clone, PartialEq)]
438#[allow(missing_copy_implementations)]
439pub struct Config {
440    pub(crate) array_sorting_mode: ArraySortingMode,
441    pub(crate) compare_mode: CompareMode,
442    pub(crate) numeric_mode: NumericMode,
443    float_compare_mode: FloatCompareMode,
444}
445
446impl Config {
447    /// Create a new [`Config`] using the given [`CompareMode`].
448    ///
449    /// The default `numeric_mode` is be [`NumericMode::Strict`].
450    pub fn new(compare_mode: CompareMode) -> Self {
451        Self {
452            array_sorting_mode: ArraySortingMode::Consider,
453            compare_mode,
454            numeric_mode: NumericMode::Strict,
455            float_compare_mode: FloatCompareMode::Exact,
456        }
457    }
458
459    /// Change the config's numeric mode.
460    ///
461    /// The default `numeric_mode` is be [`NumericMode::Strict`].
462    pub fn numeric_mode(mut self, numeric_mode: NumericMode) -> Self {
463        self.numeric_mode = numeric_mode;
464        self
465    }
466
467    /// Change the config's compare mode.
468    pub fn compare_mode(mut self, compare_mode: CompareMode) -> Self {
469        self.compare_mode = compare_mode;
470        self
471    }
472
473    /// Change the config's float compare mode.
474    ///
475    /// The default `float_compare_mode` is [`FloatCompareMode::Exact`].
476    pub fn float_compare_mode(mut self, float_compare_mode: FloatCompareMode) -> Self {
477        self.float_compare_mode = float_compare_mode;
478        self
479    }
480
481    /// configure array sorting mode
482    pub fn consider_array_sorting(mut self, consider: bool) -> Self {
483        if consider {
484            if self.compare_mode == CompareMode::Strict {
485                panic!("strict comparison does not allow array ordering to be ignored");
486            }
487            self.array_sorting_mode = ArraySortingMode::Consider;
488        } else {
489            self.array_sorting_mode = ArraySortingMode::Ignore;
490        }
491        self
492    }
493}
494
495/// Mode for how JSON values should be compared.
496#[derive(Debug, Copy, Clone, PartialEq, Eq)]
497pub enum CompareMode {
498    /// The two JSON values don't have to be exactly equal. The "expected" value is only required
499    /// to be "contained" inside "actual". See [crate documentation](index.html) for examples.
500    ///
501    /// The mode used with [`assert_json_include`].
502    Inclusive,
503    /// The two JSON values must be exactly equal.
504    ///
505    /// The mode used with [`assert_json_eq`].
506    Strict,
507}
508
509/// Should array sorting be taken in consideration
510#[derive(Debug, Copy, Clone, PartialEq, Eq)]
511enum ArraySortingMode {
512    ///consider
513    Consider,
514    /// ignore
515    Ignore,
516}
517
518/// How should numbers be compared.
519#[derive(Debug, Copy, Clone, PartialEq, Eq)]
520pub enum NumericMode {
521    /// Different numeric types aren't considered equal.
522    Strict,
523    /// All numeric types are converted to float before comparison.
524    AssumeFloat,
525}
526
527/// How should floating point numbers be compared.
528#[derive(Debug, Copy, Clone, PartialEq)]
529pub enum FloatCompareMode {
530    /// Different floats are never considered equal.
531    Exact,
532    /// Floats are considered equal if they differ by at most this epsilon value.
533    Epsilon(f64),
534}
535
536impl Eq for FloatCompareMode {}
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541    use serde_json::{json, Value};
542    use std::fmt::Write;
543
544    #[test]
545    fn boolean_root() {
546        let result = test_partial_match(json!(true), json!(true));
547        assert_output_eq(result, Ok(()));
548
549        let result = test_partial_match(json!(false), json!(false));
550        assert_output_eq(result, Ok(()));
551
552        let result = test_partial_match(json!(false), json!(true));
553        assert_output_eq(
554            result,
555            Err(r#"json atoms at path "(root)" are not equal:
556    expected:
557        true
558    actual:
559        false"#),
560        );
561
562        let result = test_partial_match(json!(true), json!(false));
563        assert_output_eq(
564            result,
565            Err(r#"json atoms at path "(root)" are not equal:
566    expected:
567        false
568    actual:
569        true"#),
570        );
571    }
572
573    #[test]
574    fn string_root() {
575        let result = test_partial_match(json!("true"), json!("true"));
576        assert_output_eq(result, Ok(()));
577
578        let result = test_partial_match(json!("false"), json!("false"));
579        assert_output_eq(result, Ok(()));
580
581        let result = test_partial_match(json!("false"), json!("true"));
582        assert_output_eq(
583            result,
584            Err(r#"json atoms at path "(root)" are not equal:
585    expected:
586        "true"
587    actual:
588        "false""#),
589        );
590
591        let result = test_partial_match(json!("true"), json!("false"));
592        assert_output_eq(
593            result,
594            Err(r#"json atoms at path "(root)" are not equal:
595    expected:
596        "false"
597    actual:
598        "true""#),
599        );
600    }
601
602    #[test]
603    fn number_root() {
604        let result = test_partial_match(json!(1), json!(1));
605        assert_output_eq(result, Ok(()));
606
607        let result = test_partial_match(json!(0), json!(0));
608        assert_output_eq(result, Ok(()));
609
610        let result = test_partial_match(json!(0), json!(1));
611        assert_output_eq(
612            result,
613            Err(r#"json atoms at path "(root)" are not equal:
614    expected:
615        1
616    actual:
617        0"#),
618        );
619
620        let result = test_partial_match(json!(1), json!(0));
621        assert_output_eq(
622            result,
623            Err(r#"json atoms at path "(root)" are not equal:
624    expected:
625        0
626    actual:
627        1"#),
628        );
629    }
630
631    #[test]
632    fn null_root() {
633        let result = test_partial_match(json!(null), json!(null));
634        assert_output_eq(result, Ok(()));
635
636        let result = test_partial_match(json!(null), json!(1));
637        assert_output_eq(
638            result,
639            Err(r#"json atoms at path "(root)" are not equal:
640    expected:
641        1
642    actual:
643        null"#),
644        );
645
646        let result = test_partial_match(json!(1), json!(null));
647        assert_output_eq(
648            result,
649            Err(r#"json atoms at path "(root)" are not equal:
650    expected:
651        null
652    actual:
653        1"#),
654        );
655    }
656
657    #[test]
658    fn into_object() {
659        let result = test_partial_match(json!({ "a": true }), json!({ "a": true }));
660        assert_output_eq(result, Ok(()));
661
662        let result = test_partial_match(json!({ "a": false }), json!({ "a": true }));
663        assert_output_eq(
664            result,
665            Err(r#"json atoms at path ".a" are not equal:
666    expected:
667        true
668    actual:
669        false"#),
670        );
671
672        let result =
673            test_partial_match(json!({ "a": { "b": true } }), json!({ "a": { "b": true } }));
674        assert_output_eq(result, Ok(()));
675
676        let result = test_partial_match(json!({ "a": true }), json!({ "a": { "b": true } }));
677        assert_output_eq(
678            result,
679            Err(r#"json atoms at path ".a" are not equal:
680    expected:
681        {
682          "b": true
683        }
684    actual:
685        true"#),
686        );
687
688        let result = test_partial_match(json!({}), json!({ "a": true }));
689        assert_output_eq(
690            result,
691            Err(r#"json atom at path ".a" is missing from actual"#),
692        );
693
694        let result = test_partial_match(json!({ "a": { "b": true } }), json!({ "a": true }));
695        assert_output_eq(
696            result,
697            Err(r#"json atoms at path ".a" are not equal:
698    expected:
699        true
700    actual:
701        {
702          "b": true
703        }"#),
704        );
705    }
706
707    #[test]
708    fn into_array() {
709        let result = test_partial_match(json!([1]), json!([1]));
710        assert_output_eq(result, Ok(()));
711
712        let result = test_partial_match(json!([2]), json!([1]));
713        assert_output_eq(
714            result,
715            Err(r#"json atoms at path "[0]" are not equal:
716    expected:
717        1
718    actual:
719        2"#),
720        );
721
722        let result = test_partial_match(json!([1, 2, 4]), json!([1, 2, 3]));
723        assert_output_eq(
724            result,
725            Err(r#"json atoms at path "[2]" are not equal:
726    expected:
727        3
728    actual:
729        4"#),
730        );
731
732        let result = test_partial_match(json!({ "a": [1, 2, 3]}), json!({ "a": [1, 2, 4]}));
733        assert_output_eq(
734            result,
735            Err(r#"json atoms at path ".a[2]" are not equal:
736    expected:
737        4
738    actual:
739        3"#),
740        );
741
742        let result = test_partial_match(json!({ "a": [1, 2, 3]}), json!({ "a": [1, 2]}));
743        assert_output_eq(result, Ok(()));
744
745        let result = test_partial_match(json!({ "a": [1, 2]}), json!({ "a": [1, 2, 3]}));
746        assert_output_eq(
747            result,
748            Err(r#"json atom at path ".a[2]" is missing from actual"#),
749        );
750    }
751
752    #[test]
753    fn exact_matching() {
754        let result = test_exact_match(json!(true), json!(true));
755        assert_output_eq(result, Ok(()));
756
757        let result = test_exact_match(json!("s"), json!("s"));
758        assert_output_eq(result, Ok(()));
759
760        let result = test_exact_match(json!("a"), json!("b"));
761        assert_output_eq(
762            result,
763            Err(r#"json atoms at path "(root)" are not equal:
764    lhs:
765        "a"
766    rhs:
767        "b""#),
768        );
769
770        let result = test_exact_match(
771            json!({ "a": [1, { "b": 2 }] }),
772            json!({ "a": [1, { "b": 3 }] }),
773        );
774        assert_output_eq(
775            result,
776            Err(r#"json atoms at path ".a[1].b" are not equal:
777    lhs:
778        2
779    rhs:
780        3"#),
781        );
782    }
783
784    #[test]
785    fn exact_match_output_message() {
786        let result = test_exact_match(json!({ "a": { "b": 1 } }), json!({ "a": {} }));
787        assert_output_eq(
788            result,
789            Err(r#"json atom at path ".a.b" is missing from rhs"#),
790        );
791
792        let result = test_exact_match(json!({ "a": {} }), json!({ "a": { "b": 1 } }));
793        assert_output_eq(
794            result,
795            Err(r#"json atom at path ".a.b" is missing from lhs"#),
796        );
797    }
798
799    fn assert_output_eq(actual: Result<(), String>, expected: Result<(), &str>) {
800        match (actual, expected) {
801            (Ok(()), Ok(())) => {}
802
803            (Err(actual_error), Ok(())) => {
804                let mut f = String::new();
805                writeln!(f, "Did not expect error, but got").unwrap();
806                writeln!(f, "{}", actual_error).unwrap();
807                panic!("{}", f);
808            }
809
810            (Ok(()), Err(expected_error)) => {
811                let expected_error = expected_error.to_string();
812                let mut f = String::new();
813                writeln!(f, "Expected error, but did not get one. Expected error:").unwrap();
814                writeln!(f, "{}", expected_error).unwrap();
815                panic!("{}", f);
816            }
817
818            (Err(actual_error), Err(expected_error)) => {
819                let expected_error = expected_error.to_string();
820                if actual_error != expected_error {
821                    let mut f = String::new();
822                    writeln!(f, "Errors didn't match").unwrap();
823                    writeln!(f, "Expected:").unwrap();
824                    writeln!(f, "{}", expected_error).unwrap();
825                    writeln!(f, "Got:").unwrap();
826                    writeln!(f, "{}", actual_error).unwrap();
827                    panic!("{}", f);
828                }
829            }
830        }
831    }
832
833    fn test_partial_match(lhs: Value, rhs: Value) -> Result<(), String> {
834        assert_json_matches_no_panic(&lhs, &rhs, &Config::new(CompareMode::Inclusive))
835    }
836
837    fn test_exact_match(lhs: Value, rhs: Value) -> Result<(), String> {
838        assert_json_matches_no_panic(&lhs, &rhs, &Config::new(CompareMode::Strict))
839    }
840}