Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 28 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@ result. Fluent assertions read like natural English.

Features of `asserting`:

1. assertions are convenient to write and easy to read
2. helpful error messages in case of failing assertions
3. colored diffs between expected and actual values
(see ["Highlighted differences"](#highlighted-differences))
4. chaining of multiple assertions on the same subject (see ["Chaining assertions"])
5. concise and expressive assertions for more complex types like collections
6. field-by-field recursive comparison (see ["Field-by-field recursive comparison"]) :new:
7. soft assertions (execute multiple assertions before panicking) (see ["Soft assertions"])
8. support for asserting custom types with provided assertions (see ["Asserting custom types"])
9. provide a reasonable number of assertions out of the box
10. do not require that asserted types have to implement traits if it is not absolutely necessary
11. custom assertions (see ["Custom assertions"](#custom-assertions))
12. support no-std environments
* assertions are convenient to write and easy to read
* helpful error messages in case of failing assertions
* colored diffs between expected and actual values
(see ["Highlighted differences"](#highlighted-differences))
* chaining of multiple assertions on the same subject (see ["Chaining assertions"])
* concise and expressive assertions for more complex types like collections
* field-by-field recursive comparison (see ["Field-by-field recursive comparison"]) :new:
* soft assertions (execute multiple assertions before panicking) (see ["Soft assertions"])
* support for asserting custom types with provided assertions (see ["Asserting custom types"])
* custom representation of values in failure reports or if a (foreign) type does not implement
[`Debug`] (see ["Type formatting (aka Representation)"])
* provide a reasonable number of assertions out of the box
* do not require that asserted types have to implement traits if it is not absolutely necessary
* custom assertions (see ["Custom assertions"](#custom-assertions))
* support no-std environments

For an overview of the provided features and many examples on how to use `asserting` see the
[crate-level documentation][docs-url].
Expand Down Expand Up @@ -71,8 +73,9 @@ default.

## Highlighted differences

`asserting` can highlight the differences between the expected value(s) and the actual value(s) when
printing assertion failures to the terminal. The colored diffs in assertion failures look like this:
`asserting` can highlight the differences between the expected value (s) and the actual value (s)
when printing assertion failures to the terminal. The colored diffs in assertion failures look like
this:

![colored diffs in terminal](examples/colored_diffs.png)

Expand All @@ -89,9 +92,9 @@ It supports different variants of how differences are highlighted.
| red-yellow | Differences are printed in the colors <span style="color: yellow">yellow</span> and <span style="color: red">red</span>. |
| off | Switches off highlighting. The differences are not highlighted at all. |

The mode can be configured by setting the environment variable `ASSERTING_HIGHLIGHT_DIFFS` to one
of the modes in the table above. The value is case-insensitive. E.g., setting the environment
variable to values like `Red-Blue`, `Bold` or `OFF` works as well.
The mode can be configured by setting the environment variable `ASSERTING_HIGHLIGHT_DIFFS` to one of
the modes in the table above. The value is case-insensitive. E.g., setting the environment variable
to values like `Red-Blue`, `Bold` or `OFF` works as well.

The intended way for configuring the highlighting mode is to set the environment variable in the
configuration for `Cargo` by adding it to the `[env]` section in your `~/.cargo/config.toml` file:
Expand Down Expand Up @@ -127,8 +130,7 @@ for all types that implement `PartialEq<E>` with `E` being the type of the expec
| is_equal_to | verify that the subject is equal to an expected value |
| is_not_equal_to | verify that the subject is not equal to a specific value |

for all types that implement `PartialEq` and the subject is of the same type as the expected
value:
for all types that implement `PartialEq` and the subject is of the same type as the expected value:

| assertion | description |
|----------------|-----------------------------------------------------------------------------------------------|
Expand Down Expand Up @@ -459,10 +461,10 @@ To start assertions on code, use the `assert_that_code!()` macro.
`asserting` provides three kinds of custom assertions:

1. use any predicate function as a custom assertion (see "[predicate as custom assertion]")
2. property-based assertions can be used with any type that implements the related property
(see "[property-based assertions]")
3. write custom assertion methods by defining and implementing an extension trait
(see "[custom assertions]")
2. property-based assertions can be used with any type that implements the related property (see
"[property-based assertions]")
3. write custom assertion methods by defining and implementing an extension trait (see
"[custom assertions]")

The mentioned references link to a chapter in the crate's documentation that describes the
possibilities for custom assertions, including examples.
Expand Down Expand Up @@ -491,6 +493,8 @@ possibilities for custom assertions, including examples.

["Field-by-field recursive comparison"]: https://docs.rs/asserting/latest/asserting/#field-by-field-recursive-comparison

["Type formatting (aka Representation)"]: https://docs.rs/asserting/latest/asserting/#type-formatting-aka-representation

["soft assertions"]: https://docs.rs/asserting/#soft-assertions

[custom assertions]: https://docs.rs/asserting/#custom-assertions
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_assertion_reusing_existing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ trait AssertSnake {
// we implement the `AssertSnake` trait for a generic `S: Borrow<Snake>` so that
// the assertion method `has_body` can be called on owned and borrowed `Snake`
// instances.
impl<S, R> AssertSnake for Spec<'_, S, R>
impl<S, D, R> AssertSnake for Spec<'_, S, D, R>
where
S: Borrow<Snake>,
R: FailingStrategy,
Expand Down
25 changes: 14 additions & 11 deletions src/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
//! assertions.
#![allow(clippy::wrong_self_convention, clippy::return_self_not_must_use)]

use crate::spec::{CollectFailures, GetFailures, Spec};
use crate::std::fmt::Debug;
use crate::spec::{CollectFailures, DebugRepresentation, GetFailures, Represent, Spec};
use crate::std::ops::RangeBounds;
use crate::std::string::String;

Expand Down Expand Up @@ -572,7 +571,7 @@ pub trait AssertOrder<E> {
/// assert_that!('r').is_in_range('H'..);
/// assert_that!('N').is_in_range(..'n');
/// ```
pub trait AssertInRange<E> {
pub trait AssertInRange<E, D> {
/// Verifies that the subject is within the expected range.
///
/// # Examples
Expand All @@ -596,7 +595,8 @@ pub trait AssertInRange<E> {
#[track_caller]
fn is_in_range<R>(self, range: R) -> Self
where
R: RangeBounds<E> + Debug;
R: RangeBounds<E>,
D: Represent<R>;

/// Verifies that the subject is not within the expected range.
///
Expand All @@ -617,7 +617,8 @@ pub trait AssertInRange<E> {
#[track_caller]
fn is_not_in_range<R>(self, range: R) -> Self
where
R: RangeBounds<E> + Debug;
R: RangeBounds<E>,
D: Represent<R>;
}

/// Assert whether a numeric value is negative or positive.
Expand Down Expand Up @@ -1364,7 +1365,7 @@ pub trait AssertEmptiness {
/// assert_that!(&some_map).has_length(4);
/// # }
/// ```
pub trait AssertHasLength<E> {
pub trait AssertHasLength<E, D> {
/// Verifies that the subject has the expected length.
///
/// # Examples
Expand Down Expand Up @@ -1475,7 +1476,8 @@ pub trait AssertHasLength<E> {
#[track_caller]
fn has_length_in_range<U>(self, expected_range: U) -> Self
where
U: RangeBounds<usize> + Debug;
U: RangeBounds<usize>,
D: Represent<U>;

/// Verifies that the subject has a length that is less than the expected
/// length.
Expand Down Expand Up @@ -1708,7 +1710,7 @@ pub trait AssertHasLength<E> {
/// assert_that!(subject).has_at_least_char_count(20);
/// assert_that!(subject).has_at_least_char_count(25);
/// ```
pub trait AssertHasCharCount<E> {
pub trait AssertHasCharCount<E, D> {
/// Verifies that the subject contains the expected number of characters.
///
/// # Examples
Expand Down Expand Up @@ -1744,7 +1746,8 @@ pub trait AssertHasCharCount<E> {
#[track_caller]
fn has_char_count_in_range<U>(self, range: U) -> Self
where
U: RangeBounds<usize> + Debug;
U: RangeBounds<usize>,
D: Represent<U>;

/// Verifies that the subject contains less than the expected number of
/// characters.
Expand Down Expand Up @@ -3934,7 +3937,7 @@ where
#[track_caller]
fn each_element<A, B>(self, assert: A) -> Self::Output
where
A: Fn(Spec<'a, <I as IntoIterator>::Item, CollectFailures>) -> B,
A: Fn(Spec<'a, <I as IntoIterator>::Item, DebugRepresentation, CollectFailures>) -> B,
B: GetFailures;

/// Iterates over the elements of a collection or an iterator and executes
Expand Down Expand Up @@ -3982,7 +3985,7 @@ where
#[track_caller]
fn any_element<A, B>(self, assert: A) -> Self::Output
where
A: Fn(Spec<'a, <I as IntoIterator>::Item, CollectFailures>) -> B,
A: Fn(Spec<'a, <I as IntoIterator>::Item, DebugRepresentation, CollectFailures>) -> B,
B: GetFailures;
}

Expand Down
34 changes: 22 additions & 12 deletions src/boolean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use crate::assertions::AssertBoolean;
use crate::colored::{mark_missing, mark_unexpected};
use crate::expectations::{IsFalse, IsTrue, is_false, is_true};
use crate::spec::{
DiffFormat, Expectation, Expecting, Expression, FailingStrategy, Invertible, Spec,
DiffFormat, Expectation, Expecting, Expression, FailingStrategy, Invertible, Represent,
Represented, Spec,
};
use crate::std::format;
use crate::std::string::String;

impl<R> AssertBoolean for Spec<'_, bool, R>
impl<D, R> AssertBoolean for Spec<'_, bool, D, R>
where
D: Represent<bool>,
R: FailingStrategy,
{
fn is_true(self) -> Self {
Expand All @@ -22,7 +24,10 @@ where
}
}

impl Expectation<bool> for IsTrue {
impl<D> Expectation<bool, D> for IsTrue
where
D: Represent<bool>,
{
fn test(&mut self, subject: &bool) -> bool {
*subject
}
Expand All @@ -32,20 +37,24 @@ impl Expectation<bool> for IsTrue {
expression: &Expression<'_>,
actual: &bool,
inverted: bool,
representation: &D,
format: &DiffFormat,
) -> String {
let marked_actual = mark_unexpected(&actual, format);
let marked_expected = mark_missing(&!inverted, format);
let marked_actual = mark_unexpected(actual, representation, format);
let marked_expected = mark_missing(&!inverted, representation, format);
let represented_expected = Represented::from((&true, representation));
format!(
"expected {expression} to be {:?}\n but was: {marked_actual}\n expected: {marked_expected}",
true
"expected {expression} to be {represented_expected:?}\n but was: {marked_actual}\n expected: {marked_expected}",
)
}
}

impl Invertible for IsTrue {}

impl Expectation<bool> for IsFalse {
impl<D> Expectation<bool, D> for IsFalse
where
D: Represent<bool>,
{
fn test(&mut self, subject: &bool) -> bool {
!*subject
}
Expand All @@ -55,13 +64,14 @@ impl Expectation<bool> for IsFalse {
expression: &Expression<'_>,
actual: &bool,
inverted: bool,
representation: &D,
format: &DiffFormat,
) -> String {
let marked_actual = mark_unexpected(actual, format);
let marked_expected = mark_missing(&inverted, format);
let marked_actual = mark_unexpected(actual, representation, format);
let marked_expected = mark_missing(&inverted, representation, format);
let represented_expected = Represented::from((&false, representation));
format!(
"expected {expression} to be {:?}\n but was: {marked_actual}\n expected: {marked_expected}",
false
"expected {expression} to be {represented_expected:?}\n but was: {marked_actual}\n expected: {marked_expected}",
)
}
}
Expand Down
Loading
Loading