Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  lib.rs

  Sprache: Rust
 

//! This crate includes macros for comparing two serializable values by diffing their JSON
//! representations. It is designed to give much more helpful error messages than the standard
//! [`assert_eq!`]. It basically does a diff of the two objects and tells you the exact
//! differences. This is useful when asserting that two large JSON objects are the same.
//!
//! It uses the [serde] and [serde_json] to perform the serialization.
//!
//! [serde]: https://crates.io/crates/serde
//! [serde_json]: https://crates.io/crates/serde_json
//! [`assert_eq!`]: https://doc.rust-lang.org/std/macro.assert_eq.html
//!
//! ## Partial matching
//!
//! If you want to assert that one JSON value is "included" in another use
//! [`assert_json_include`](macro.assert_json_include.html):
//!
//! ```should_panic
//! use assert_json_diff::assert_json_include;
//! use serde_json::json;
//!
//! let a = json!({
//!     "data": {
//!         "users": [
//!             {
//!                 "id": 1,
//!                 "country": {
//!                     "name": "Denmark"
//!                 }
//!             },
//!             {
//!                 "id": 24,
//!                 "country": {
//!                     "name": "Denmark"
//!                 }
//!             }
//!         ]
//!     }
//! });
//!
//! let b = json!({
//!     "data": {
//!         "users": [
//!             {
//!                 "id": 1,
//!                 "country": {
//!                     "name": "Sweden"
//!                 }
//!             },
//!             {
//!                 "id": 2,
//!                 "country": {
//!                     "name": "Denmark"
//!                 }
//!             }
//!         ]
//!     }
//! });
//!
//! assert_json_include!(actual: a, expected: b)
//! ```
//!
//! This will panic with the error message:
//!
//! ```text
//! json atoms at path ".data.users[0].country.name" are not equal:
//!     expected:
//!         "Sweden"
//!     actual:
//!         "Denmark"
//!
//! json atoms at path ".data.users[1].id" are not equal:
//!     expected:
//!         2
//!     actual:
//!         24
//! ```
//!
//! [`assert_json_include`](macro.assert_json_include.html) allows extra data in `actual` but not in `expected`. That is so you can verify just a part
//! of the JSON without having to specify the whole thing. For example this test passes:
//!
//! ```
//! use assert_json_diff::assert_json_include;
//! use serde_json::json;
//!
//! assert_json_include!(
//!     actual: json!({
//!         "a": { "b": 1 },
//!     }),
//!     expected: json!({
//!         "a": {},
//!     })
//! )
//! ```
//!
//! However `expected` cannot contain additional data so this test fails:
//!
//! ```should_panic
//! use assert_json_diff::assert_json_include;
//! use serde_json::json;
//!
//! assert_json_include!(
//!     actual: json!({
//!         "a": {},
//!     }),
//!     expected: json!({
//!         "a": { "b": 1 },
//!     })
//! )
//! ```
//!
//! That will print
//!
//! ```text
//! json atom at path ".a.b" is missing from actual
//! ```
//!
//! ## Exact matching
//!
//! If you want to ensure two JSON values are *exactly* the same, use [`assert_json_eq`](macro.assert_json_eq.html).
//!
//! ```rust,should_panic
//! use assert_json_diff::assert_json_eq;
//! use serde_json::json;
//!
//! assert_json_eq!(
//!     json!({ "a": { "b": 1 } }),
//!     json!({ "a": {} })
//! )
//! ```
//!
//! This will panic with the error message:
//!
//! ```text
//! json atom at path ".a.b" is missing from lhs
//! ```
//!
//! ## Further customization
//!
//! You can use [`assert_json_matches`] to further customize the comparison.

#![deny(
    missing_docs,
    unused_imports,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications,
    unknown_lints
)]

use diff::diff;
use serde::Serialize;

mod core_ext;
mod diff;

/// Compare two JSON values for an inclusive match.
///
/// It allows `actual` to contain additional data. If you want an exact match use
/// [`assert_json_eq`](macro.assert_json_eq.html) instead.
///
/// See [crate documentation](index.html) for examples.
#[macro_export]
macro_rules! assert_json_include {
    (actual: $actual:expr, expected: $expected:expr $(,)?) => {{
        $crate::assert_json_matches!(
            $actual,
            $expected,
            $crate::Config::new($crate::CompareMode::Inclusive)
        )
    }};
    (expected: $expected:expr, actual: $actual:expr $(,)?) => {{
        $crate::assert_json_include!(actual: $actual, expected: $expected)
    }};
}

/// Compare two JSON values for an exact match.
///
/// If you want an inclusive match use [`assert_json_include`](macro.assert_json_include.html) instead.
///
/// See [crate documentation](index.html) for examples.
#[macro_export]
macro_rules! assert_json_eq {
    ($lhs:expr, $rhs:expr $(,)?) => {{
        $crate::assert_json_matches!($lhs, $rhs, $crate::Config::new($crate::CompareMode::Strict))
    }};
}

/// Compare two JSON values according to a configuration.
///
/// ```
/// use assert_json_diff::{
///     CompareMode,
///     Config,
///     NumericMode,
///     assert_json_matches,
/// };
/// use serde_json::json;
///
/// let config = Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat);
///
/// assert_json_matches!(
///     json!({
///         "a": { "b": [1, 2, 3.0] },
///     }),
///     json!({
///         "a": { "b": [1, 2.0, 3] },
///     }),
///     config,
/// )
/// ```
///
/// When using `CompareMode::Inclusive` the first argument is `actual` and the second argument is
/// `expected`. Example:
///
/// ```
/// # use assert_json_diff::{
/// #     CompareMode,
/// #     Config,
/// #     NumericMode,
/// #     assert_json_matches,
/// #     assert_json_include,
/// # };
/// # use serde_json::json;
/// #
/// // This
/// assert_json_matches!(
///     json!({
///         "a": { "b": 1 },
///     }),
///     json!({
///         "a": {},
///     }),
///     Config::new(CompareMode::Inclusive),
/// );
///
/// // Is the same as this
/// assert_json_include!(
///     actual: json!({
///         "a": { "b": 1 },
///     }),
///     expected: json!({
///         "a": {},
///     }),
/// );
/// ```
#[macro_export]
macro_rules! assert_json_matches {
    ($lhs:expr, $rhs:expr, $config:expr $(,)?) => {{
        if let Err(error) = $crate::assert_json_matches_no_panic(&$lhs, &$rhs, $config) {
            panic!("\n\n{}\n\n", error);
        }
    }};
}

/// Compares two JSON values without panicking.
///
/// Instead it returns a `Result` where the error is the message that would be passed to `panic!`.
/// This is might be useful if you want to control how failures are reported and don't want to deal
/// with panics.
pub fn assert_json_matches_no_panic<Lhs, Rhs>(
    lhs: &Lhs,
    rhs: &Rhs,
    config: Config,
) -> Result<(), String>
where
    Lhs: Serialize,
    Rhs: Serialize,
{
    let lhs = serde_json::to_value(lhs).unwrap_or_else(|err| {
        panic!(
            "Couldn't convert left hand side value to JSON. Serde error: {}",
            err
        )
    });
    let rhs = serde_json::to_value(rhs).unwrap_or_else(|err| {
        panic!(
            "Couldn't convert right hand side value to JSON. Serde error: {}",
            err
        )
    });

    let diffs = diff(&lhs, &rhs, config);

    if diffs.is_empty() {
        Ok(())
    } else {
        let msg = diffs
            .into_iter()
            .map(|d| d.to_string())
            .collect::<Vec<_>>()
            .join("\n\n");
        Err(msg)
    }
}

/// Configuration for how JSON values should be compared.
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(missing_copy_implementations)]
pub struct Config {
    pub(crate) compare_mode: CompareMode,
    pub(crate) numeric_mode: NumericMode,
}

impl Config {
    /// Create a new [`Config`] using the given [`CompareMode`].
    ///
    /// The default `numeric_mode` is be [`NumericMode::Strict`].
    pub fn new(compare_mode: CompareMode) -> Self {
        Self {
            compare_mode,
            numeric_mode: NumericMode::Strict,
        }
    }

    /// Change the config's numeric mode.
    ///
    /// The default `numeric_mode` is be [`NumericMode::Strict`].
    pub fn numeric_mode(mut self, numeric_mode: NumericMode) -> Self {
        self.numeric_mode = numeric_mode;
        self
    }

    /// Change the config's compare mode.
    pub fn compare_mode(mut self, compare_mode: CompareMode) -> Self {
        self.compare_mode = compare_mode;
        self
    }
}

/// Mode for how JSON values should be compared.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CompareMode {
    /// The two JSON values don't have to be exactly equal. The "actual" value is only required to
    /// be "contained" inside "expected". See [crate documentation](index.html) for examples.
    ///
    /// The mode used with [`assert_json_include`].
    Inclusive,
    /// The two JSON values must be exactly equal.
    ///
    /// The mode used with [`assert_json_eq`].
    Strict,
}

/// How should numbers be compared.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NumericMode {
    /// Different numeric types aren't considered equal.
    Strict,
    /// All numeric types are converted to float before comparison.
    AssumeFloat,
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::{json, Value};
    use std::fmt::Write;

    #[test]
    fn boolean_root() {
        let result = test_partial_match(json!(true), json!(true));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!(false), json!(false));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!(false), json!(true));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        true
    actual:
        false"#),
        );

        let result = test_partial_match(json!(true), json!(false));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        false
    actual:
        true"#),
        );
    }

    #[test]
    fn string_root() {
        let result = test_partial_match(json!("true"), json!("true"));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!("false"), json!("false"));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!("false"), json!("true"));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        "true"
    actual:
        "false""#),
        );

        let result = test_partial_match(json!("true"), json!("false"));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        "false"
    actual:
        "true""#),
        );
    }

    #[test]
    fn number_root() {
        let result = test_partial_match(json!(1), json!(1));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!(0), json!(0));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!(0), json!(1));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        1
    actual:
        0"#),
        );

        let result = test_partial_match(json!(1), json!(0));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        0
    actual:
        1"#),
        );
    }

    #[test]
    fn null_root() {
        let result = test_partial_match(json!(null), json!(null));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!(null), json!(1));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        1
    actual:
        null"#),
        );

        let result = test_partial_match(json!(1), json!(null));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    expected:
        null
    actual:
        1"#),
        );
    }

    #[test]
    fn into_object() {
        let result = test_partial_match(json!({ "a"true }), json!({ "a"true }));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!({ "a"false }), json!({ "a"true }));
        assert_output_eq(
            result,
            Err(r#"json atoms at path ".a" are not equal:
    expected:
        true
    actual:
        false"#),
        );

        let result =
            test_partial_match(json!({ "a": { "b"true } }), json!({ "a": { "b"true } }));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!({ "a"true }), json!({ "a": { "b"true } }));
        assert_output_eq(
            result,
            Err(r#"json atoms at path ".a" are not equal:
    expected:
        {
          "b"true
        }
    actual:
        true"#),
        );

        let result = test_partial_match(json!({}), json!({ "a"true }));
        assert_output_eq(
            result,
            Err(r#"json atom at path ".a" is missing from actual"#),
        );

        let result = test_partial_match(json!({ "a": { "b"true } }), json!({ "a"true }));
        assert_output_eq(
            result,
            Err(r#"json atoms at path ".a" are not equal:
    expected:
        true
    actual:
        {
          "b"true
        }"#),
        );
    }

    #[test]
    fn into_array() {
        let result = test_partial_match(json!([1]), json!([1]));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!([2]), json!([1]));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "[0]" are not equal:
    expected:
        1
    actual:
        2"#),
        );

        let result = test_partial_match(json!([124]), json!([123]));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "[2]" are not equal:
    expected:
        3
    actual:
        4"#),
        );

        let result = test_partial_match(json!({ "a": [123]}), json!({ "a": [124]}));
        assert_output_eq(
            result,
            Err(r#"json atoms at path ".a[2]" are not equal:
    expected:
        4
    actual:
        3"#),
        );

        let result = test_partial_match(json!({ "a": [123]}), json!({ "a": [12]}));
        assert_output_eq(result, Ok(()));

        let result = test_partial_match(json!({ "a": [12]}), json!({ "a": [123]}));
        assert_output_eq(
            result,
            Err(r#"json atom at path ".a[2]" is missing from actual"#),
        );
    }

    #[test]
    fn exact_matching() {
        let result = test_exact_match(json!(true), json!(true));
        assert_output_eq(result, Ok(()));

        let result = test_exact_match(json!("s"), json!("s"));
        assert_output_eq(result, Ok(()));

        let result = test_exact_match(json!("a"), json!("b"));
        assert_output_eq(
            result,
            Err(r#"json atoms at path "(root)" are not equal:
    lhs:
        "a"
    rhs:
        "b""#),
        );

        let result = test_exact_match(
            json!({ "a": [1, { "b"2 }] }),
            json!({ "a": [1, { "b"3 }] }),
        );
        assert_output_eq(
            result,
            Err(r#"json atoms at path ".a[1].b" are not equal:
    lhs:
        2
    rhs:
        3"#),
        );
    }

    #[test]
    fn exact_match_output_message() {
        let result = test_exact_match(json!({ "a": { "b"1 } }), json!({ "a": {} }));
        assert_output_eq(
            result,
            Err(r#"json atom at path ".a.b" is missing from rhs"#),
        );

        let result = test_exact_match(json!({ "a": {} }), json!({ "a": { "b"1 } }));
        assert_output_eq(
            result,
            Err(r#"json atom at path ".a.b" is missing from lhs"#),
        );
    }

    fn assert_output_eq(actual: Result<(), String>, expected: Result<(), &str>) {
        match (actual, expected) {
            (Ok(()), Ok(())) => {}

            (Err(actual_error), Ok(())) => {
                let mut f = String::new();
                writeln!(f, "Did not expect error, but got").unwrap();
                writeln!(f, "{}", actual_error).unwrap();
                panic!("{}", f);
            }

            (Ok(()), Err(expected_error)) => {
                let expected_error = expected_error.to_string();
                let mut f = String::new();
                writeln!(f, "Expected error, but did not get one. Expected error:").unwrap();
                writeln!(f, "{}", expected_error).unwrap();
                panic!("{}", f);
            }

            (Err(actual_error), Err(expected_error)) => {
                let expected_error = expected_error.to_string();
                if actual_error != expected_error {
                    let mut f = String::new();
                    writeln!(f, "Errors didn't match").unwrap();
                    writeln!(f, "Expected:").unwrap();
                    writeln!(f, "{}", expected_error).unwrap();
                    writeln!(f, "Got:").unwrap();
                    writeln!(f, "{}", actual_error).unwrap();
                    panic!("{}", f);
                }
            }
        }
    }

    fn test_partial_match(lhs: Value, rhs: Value) -> Result<(), String> {
        assert_json_matches_no_panic(&lhs, &rhs, Config::new(CompareMode::Inclusive))
    }

    fn test_exact_match(lhs: Value, rhs: Value) -> Result<(), String> {
        assert_json_matches_no_panic(&lhs, &rhs, Config::new(CompareMode::Strict))
    }
}

Messung V0.5 in Prozent
C=82 H=84 G=82

¤ Dauer der Verarbeitung: 0.44 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002