test_better/lib.rs
1//! `test-better`: `Result`-returning Rust tests with `?`.
2//!
3//! `test-better` makes a test that returns [`TestResult`] and uses `?` strictly
4//! better than a panicking one: a failure is a value that carries the
5//! expression that failed, the values involved, the source location, and the
6//! context you attached on the way down.
7//!
8//! This is the facade crate, the one users depend on. It re-exports the public
9//! surface of the workspace's focused crates (`test-better-core`,
10//! `test-better-matchers`, and so on) so a test file needs a single
11//! dependency and, ideally, a single `use`:
12//!
13//! ```
14//! use test_better::prelude::*;
15//!
16//! fn parse_port(input: &str) -> Option<u16> {
17//! input.parse().ok()
18//! }
19//!
20//! # fn main() -> TestResult {
21//! // `or_fail_with` is the `?`-friendly stand-in for a panicking unwrap;
22//! // `check!` captures the expression text so a failure names `port`,
23//! // not just its value.
24//! let port = parse_port("8080").or_fail_with("8080 is a valid port")?;
25//! check!(port).satisfies(eq(8080))?;
26//! check!(port).violates(lt(1024))?;
27//! # Ok(())
28//! # }
29//! ```
30//!
31//! In a real test file the body above is a `#[test] fn ... -> TestResult`
32//! ending in `Ok(())`. See the [`prelude`] for the one import a test file
33//! needs, and the [`cookbook`] for writing custom matchers. The prose guide
34//! (Getting Started, migrating from the stock assertion macros, async,
35//! property, snapshot, and fixture testing) is the `test-better` book under
36//! `book/`.
37
38pub use test_better_core::{
39 ColorChoice, ContextExt, ContextFrame, ErrorKind, OrFail, Payload, RUNNER_ENV,
40 STRUCTURED_MARKER, SourceLocation, StructuredContextFrame, StructuredError, StructuredPayload,
41 TestError, TestResult, Trace, TraceEntry, color_choice, set_color_choice,
42};
43pub use test_better_macros::{
44 fixture, matches_struct, matches_tuple, matches_variant, test_case, test_with_fixtures,
45};
46// The property-testing bridge. `Config` is renamed `PropertyConfig` here: at
47// the facade root, where one crate's surface meets eight others, a bare
48// `Config` says too little.
49#[cfg(feature = "diff")]
50pub use test_better_matchers::diff_lines;
51#[cfg(feature = "regex")]
52pub use test_better_matchers::matches_regex;
53pub use test_better_matchers::{
54 Backoff, ContainsAll, Description, Elapsed, Float, Items, MatchResult, Matcher, MatcherTuple,
55 Mismatch, RuntimeAvailable, Sequence, SoftAsserter, SoftScope, Subject, all_of, always_matches,
56 any_of, at_least_one, between, check, close_to, contains, contains_all, contains_in_order,
57 contains_str, define_matcher, ends_with, eq, err, eventually, eventually_blocking,
58 eventually_blocking_with, eventually_with, every, ge, gt, have_len, is_empty, is_false,
59 is_finite, is_nan, is_not_empty, is_true, items, le, lt, ne, never_matches, none, not, ok,
60 predicate, soft, some, starts_with,
61};
62pub use test_better_property::{
63 Config as PropertyConfig, GenError, PropertyFailure, ProptestTree, Runner, Strategy, ValueTree,
64 any, for_all, for_all_with, property,
65};
66// The best-effort `quickcheck` bridge, behind the facade's `quickcheck`
67// feature: `arbitrary::<T>()` turns a `quickcheck::Arbitrary` type into a
68// `Strategy<T>`. Off by default; `proptest` is the primary backend.
69#[cfg(feature = "quickcheck")]
70pub use test_better_property::{ArbitraryStrategy, QuickcheckTree, arbitrary};
71// The snapshot store. The everyday entry points are the
72// `check!(value).matches_snapshot("name")` and `.matches_inline_snapshot(..)`
73// methods (on the re-exported `Subject`); these are the lower-level pieces they
74// are built on, for callers that need an explicit directory or mode, or that
75// drive the `test-better-accept` companion binary.
76pub use test_better_snapshot::{
77 InlineLocation, InlineSnapshotFailure, Redactions, SnapshotFailure, SnapshotMode,
78 assert_inline_snapshot, assert_snapshot, assert_snapshot_in, normalize_inline_literal,
79 parse_pending_patch, pending_patch_dir, snapshot_path,
80};
81
82/// How to write a custom matcher: see the [`cookbook`] module.
83pub mod cookbook;
84
85/// The one `use` a test file should need: `use test_better::prelude::*;`.
86///
87/// The prelude is deliberately small. It brings in the result type, the error
88/// type, the extension traits whose methods (`context`, `or_fail`, ...) are
89/// meant to be called without qualification, the `check!` macro, and the
90/// matcher constructors (`eq`, `lt`, ...). The structured-failure types and the
91/// custom-matcher machinery (`Matcher`, `Description`, ...) stay out of it:
92/// they are imported by name when needed, not in the body of every test.
93///
94/// # Re-exporting macros
95///
96/// `#[macro_export]` places a macro at the crate root, not inside the module it
97/// is written in, so a glob import of this module would *not* pick it up unless
98/// the macro is named here explicitly. That is why `check` and
99/// `define_matcher` appear below with `pub use crate::...;`; later phases add
100/// their `#[macro_export]` macros the same way.
101///
102/// Procedural macros (`matches_struct!`, `matches_tuple!`, `matches_variant!`,
103/// and the `#[fixture]` / `#[test_with_fixtures]` attribute pair) are different:
104/// they are ordinary items of `test-better-macros`, so a plain `pub use`
105/// re-exports them and they need no special treatment. `#[fixture]` and
106/// `#[test_with_fixtures]` are in the prelude (unlike `#[test_case]`, they do
107/// not collide with anything in `std`'s prelude).
108///
109/// The one exception is `#[test_case]`: it lives at the facade root
110/// (`test_better::test_case`) but is kept *out* of the prelude, because `std`'s
111/// prelude exports a `test_case` attribute of its own and two glob imports of
112/// one name are ambiguous. Import it by name.
113pub mod prelude {
114 pub use test_better_core::{ContextExt, OrFail, TestError, TestResult};
115 #[cfg(feature = "regex")]
116 pub use test_better_matchers::matches_regex;
117 pub use test_better_matchers::{
118 all_of, always_matches, any_of, at_least_one, between, close_to, contains, contains_all,
119 contains_in_order, contains_str, ends_with, eq, err, eventually, eventually_blocking,
120 every, ge, gt, have_len, is_empty, is_false, is_finite, is_nan, is_not_empty, is_true,
121 items, le, lt, ne, never_matches, none, not, ok, predicate, soft, some, starts_with,
122 };
123
124 // `test_case` is deliberately *not* re-exported here. `std`'s own prelude
125 // already exports a `test_case` attribute (the unstable custom-test-
126 // frameworks one), and two glob imports of the same name are ambiguous at
127 // the use site. It is available as `test_better::test_case`; import it
128 // explicitly.
129 pub use test_better_macros::{
130 fixture, matches_struct, matches_tuple, matches_variant, test_with_fixtures,
131 };
132
133 pub use crate::{check, define_matcher, property};
134}