Skip to main content

Subject

Struct Subject 

Source
pub struct Subject<T> { /* private fields */ }
Expand description

A value under test, paired with the source text of the expression that produced it.

Subject owns its value (the check! macro hands it over by value) and borrows nothing, so it carries no lifetime parameter.

Implementations§

Source§

impl<T> Subject<T>

Source

pub fn new( actual: T, expr: &'static str, module_path: &'static str, ) -> Subject<T>

Pairs actual with the source text it came from and the module_path!() of the call site. Called by check!; rarely constructed directly.

module_path is only consulted by matches_snapshot, which uses it to name the snapshot file; every other method ignores it.

Source

pub fn satisfies<M>(self, matcher: M) -> Result<(), TestError>
where M: Matcher<T>,

Asserts that the value satisfies matcher.

Returns Ok(()) on a match and a TestError otherwise. The result is #[must_use] (it is a Result), so a forgotten ? is a compiler warning rather than a silently-passing assertion.

Source

pub fn violates<M>(self, matcher: M) -> Result<(), TestError>
where M: Matcher<T>,

Asserts that the value does not satisfy matcher.

Returns Ok(()) when the matcher does not match, and a TestError when it unexpectedly does. Equivalent to satisfies(not(matcher)); pick whichever reads better at the call site.

Source

pub fn resolves_to<M>( self, matcher: M, ) -> impl Future<Output = Result<(), TestError>>
where T: Future, M: Matcher<<T as Future>::Output>,

Awaits the future and asserts that its output satisfies matcher.

This is the async counterpart of satisfies: reach for it when the expression handed to check! is a Future. The matcher runs against the future’s output, so check!(fut).resolves_to(eq(4)) is exactly check!(fut.await).satisfies(eq(4)) without the intermediate binding.

The method itself is not async: it is #[track_caller] and returns a future. The call-site location is captured synchronously when resolves_to is called (an async fn could not be #[track_caller]), then carried into the failure once the returned future is awaited.

use test_better_core::TestResult;
use test_better_matchers::{check, eq};

pollster::block_on(async {
    check!(async { 2 + 2 }).resolves_to(eq(4)).await?;
    TestResult::Ok(())
})
Source

pub fn completes_within( self, limit: Duration, ) -> impl Future<Output = Result<(), TestError>>

Awaits the future, but fails if it does not finish within limit.

Like resolves_to, this is for a future-typed subject and returns a future rather than being async itself, so the #[track_caller] location is the call site. Unlike resolves_to, it does not look at the output: the assertion is purely about time.

The timeout needs a runtime to provide its sleep, selected by a cargo feature on test-better: tokio, async-std, or smol. With none enabled, the T: RuntimeAvailable bound is unsatisfied and the call is a compile error naming those flags.

use std::time::Duration;
use test_better::prelude::*;

check!(some_future())
    .completes_within(Duration::from_millis(50))
    .await?;
Source

pub fn matches_snapshot(self, name: &str) -> Result<(), TestError>
where T: Display,

Asserts that the value matches the snapshot stored under name.

The snapshot lives at tests/snapshots/<module-path>__<name>.snap in the package under test, with <module-path> taken from the call site’s module_path!(). On a normal run the value is compared against that file; a difference (or a missing file) is a Snapshot failure, and the difference is rendered with the standard line-oriented diff. Rerun the test with UPDATE_SNAPSHOTS=1 to create the file the first time, or to accept an intentional change.

The value only has to be Display: that is what gets written to and compared against the file.

use test_better_core::TestResult;
use test_better_matchers::check;

// Run once with `UPDATE_SNAPSHOTS=1` to record the snapshot; later runs
// compare against `tests/snapshots/<module>__homepage.snap`.
check!("<h1>Hello</h1>").matches_snapshot("homepage")?;
Source

pub fn matches_snapshot_with( self, name: &str, redactions: &Redactions, ) -> Result<(), TestError>
where T: Display,

Like matches_snapshot, but runs redactions over the value first.

Use this when the rendered value carries content that is not stable run to run (a generated UUID, a timestamp): the redactions rewrite those spans to fixed placeholders, so the snapshot file stays meaningful. Because the redactions run on every comparison, the placeholder is what is stored and what later runs compare against.

use test_better_core::TestResult;
use test_better_matchers::check;
use test_better_snapshot::Redactions;

let rendered = format!("created {}", uuid_of_new_record());
check!(rendered)
    .matches_snapshot_with("record", &Redactions::new().redact_uuids())?;
Source

pub fn matches_inline_snapshot(self, expected: &str) -> Result<(), TestError>
where T: Display,

Asserts that the value matches the inline snapshot literal expected.

Unlike matches_snapshot, the snapshot lives in the test source: expected is the snapshot. The literal is normalized before comparison (a leading newline and the common indentation are cosmetic), so it can be indented to match the surrounding code.

On a mismatch with UPDATE_SNAPSHOTS unset this fails like any assertion. With UPDATE_SNAPSHOTS=1 it records a pending patch under target/test-better-pending/ and passes; the test-better-accept companion binary rewrites the literal in the source from those patches. A proc macro could not do this rewrite (it runs before the test), so the work is split: this method captures the call-site location with #[track_caller], the accept binary edits the file.

use test_better_core::TestResult;
use test_better_matchers::check;

check!(2 + 2).matches_inline_snapshot("4")?;
Source

pub fn matches_inline_snapshot_with( self, expected: &str, redactions: &Redactions, ) -> Result<(), TestError>
where T: Display,

Like matches_inline_snapshot, but runs redactions over the value first.

The inline-snapshot counterpart of matches_snapshot_with: redaction rewrites non-deterministic spans to fixed placeholders before the comparison, so UPDATE_SNAPSHOTS=1 records the redacted value as the literal and later runs stay green.

use test_better_core::TestResult;
use test_better_matchers::check;
use test_better_snapshot::Redactions;

let rendered = format!("at {}", now_rfc3339());
check!(rendered).matches_inline_snapshot_with(
    "at [timestamp]",
    &Redactions::new().redact_rfc3339_timestamps(),
)?;

Auto Trait Implementations§

§

impl<T> Freeze for Subject<T>
where T: Freeze,

§

impl<T> RefUnwindSafe for Subject<T>
where T: RefUnwindSafe,

§

impl<T> Send for Subject<T>
where T: Send,

§

impl<T> Sync for Subject<T>
where T: Sync,

§

impl<T> Unpin for Subject<T>
where T: Unpin,

§

impl<T> UnsafeUnpin for Subject<T>
where T: UnsafeUnpin,

§

impl<T> UnwindSafe for Subject<T>
where T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> RuntimeAvailable for T
where T: ?Sized,