snapbox/lib.rs
1//! # Snapshot testing toolbox
2//!
3//! > When you have to treat your tests like pets, instead of [cattle][trycmd]
4//!
5//! `snapbox` is a snapshot-testing toolbox that is ready to use for verifying output from
6//! - Function return values
7//! - CLI stdout/stderr
8//! - Filesystem changes
9//!
10//! It is also flexible enough to build your own test harness like [trycmd](https://crates.io/crates/trycmd).
11//!
12//! ## Which tool is right
13//!
14//! - [cram](https://bitheap.org/cram/): End-to-end CLI snapshotting agnostic of any programming language
15//! - See also [scrut](https://github.com/facebookincubator/scrut)
16//! - [trycmd](https://crates.io/crates/trycmd): For running a lot of blunt tests (limited test predicates)
17//! - Particular attention is given to allow the test data to be pulled into documentation, like
18//! with [mdbook](https://rust-lang.github.io/mdBook/)
19//! - [tryfn](https://crates.io/crates/tryfn): For running a lot of simple input/output tests
20//! - `snapbox`: When you want something like `trycmd` in one off
21//! cases or you need to customize `trycmd`s behavior.
22//! - [assert_cmd](https://crates.io/crates/assert_cmd) +
23//! [assert_fs](https://crates.io/crates/assert_fs): Test cases follow a certain pattern but
24//! special attention is needed in how to verify the results.
25//! - Hand-written test cases: for peculiar circumstances
26//!
27//! ## Getting Started
28//!
29//! Testing Functions:
30//! - [`assert_data_eq!`] for quick and dirty snapshotting
31//!
32//! Testing Commands:
33//! - [`cmd::Command`]: Process spawning for testing of non-interactive commands
34//! - [`cmd::OutputAssert`]: Assert the state of a [`Command`][cmd::Command]'s
35//! [`Output`][std::process::Output].
36//!
37//! Testing Filesystem Interactions:
38//! - [`dir::DirRoot`]: Working directory for tests
39//! - [`Assert`]: Diff a directory against files present in a pattern directory
40//!
41//! You can also build your own version of these with the lower-level building blocks these are
42//! made of.
43//!
44#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
45//!
46//! # Examples
47//!
48//! [`assert_data_eq!`]
49//! ```rust
50//! snapbox::assert_data_eq!("Hello many people!", "Hello [..] people!");
51//! ```
52//!
53//! [`Assert`]
54//! ```rust,no_run
55//! let actual = "...";
56//! snapbox::Assert::new()
57//! .action_env("SNAPSHOTS")
58//! .eq(actual, snapbox::file!["help_output_is_clean.txt"]);
59//! ```
60//!
61//! [trycmd]: https://docs.rs/trycmd
62
63#![cfg_attr(docsrs, feature(doc_auto_cfg))]
64#![warn(clippy::print_stderr)]
65#![warn(clippy::print_stdout)]
66
67mod macros;
68
69pub mod assert;
70pub mod cmd;
71pub mod data;
72pub mod dir;
73pub mod filter;
74pub mod report;
75pub mod utils;
76
77pub use assert::Assert;
78pub use data::Data;
79pub use data::IntoData;
80#[cfg(feature = "json")]
81pub use data::IntoJson;
82pub use data::ToDebug;
83pub use filter::RedactedValue;
84pub use filter::Redactions;
85#[doc(hidden)]
86pub use snapbox_macros::debug;
87
88/// Easier access to common traits
89pub mod prelude {
90 pub use crate::IntoData;
91 #[cfg(feature = "json")]
92 pub use crate::IntoJson;
93 pub use crate::ToDebug;
94}
95
96/// Check if a path matches the content of another path, recursively
97///
98/// When the content is text, newlines are normalized.
99///
100/// ```rust,no_run
101/// let output_root = "...";
102/// let expected_root = "tests/snapshots/output.txt";
103/// snapbox::assert_subset_eq(expected_root, output_root);
104/// ```
105#[cfg(feature = "dir")]
106#[track_caller]
107pub fn assert_subset_eq(
108 expected_root: impl Into<std::path::PathBuf>,
109 actual_root: impl Into<std::path::PathBuf>,
110) {
111 Assert::new()
112 .action_env(assert::DEFAULT_ACTION_ENV)
113 .subset_eq(expected_root, actual_root);
114}
115
116/// Check if a path matches the pattern of another path, recursively
117///
118/// Pattern syntax:
119/// - `...` is a line-wildcard when on a line by itself
120/// - `[..]` is a character-wildcard when inside a line
121/// - `[EXE]` matches `.exe` on Windows
122///
123/// Normalization:
124/// - Newlines
125/// - `\` to `/`
126///
127/// ```rust,no_run
128/// let output_root = "...";
129/// let expected_root = "tests/snapshots/output.txt";
130/// snapbox::assert_subset_matches(expected_root, output_root);
131/// ```
132#[cfg(feature = "dir")]
133#[track_caller]
134pub fn assert_subset_matches(
135 pattern_root: impl Into<std::path::PathBuf>,
136 actual_root: impl Into<std::path::PathBuf>,
137) {
138 Assert::new()
139 .action_env(assert::DEFAULT_ACTION_ENV)
140 .subset_matches(pattern_root, actual_root);
141}