Expand description
Fluent assertions for tests in Rust that are convenient to write and easy to extend.
Fluent assertions have some significant advantages - in general and particularly as provided by this crate:
- express the intent of an assertion
- an assertion reads more like natural english
- concise and expressive assertions for more complex types like collections
- distinct and more helpful error messages for specific assertions
- easy spotting the difference between the expected and the actual value
- chaining of multiple assertions on the same subject
- soft assertions
An additional benefit of asserting
is that it highlights differences
between the expected value and the actual value for failed assertions.
See the documentation of the colored
module for more information on
“colored diffs”.
§Usage
To write fluent assertions in tests, import this crate’s prelude
module in your test module, like so:
use asserting::prelude::*;
Importing the prelude
module is the intended way to use this crate. The
prelude re-exports all types, traits and functions and macros that are
needed to write assertions in tests.
Start writing assertion by applying the assert_that!
macro to the subject
to be asserted. Then call an assertion
function like is_equal_to
, like so:
let some_result = 7 * 6;
assert_that!(some_result).is_equal_to(42);
The subject can be any expression, e.g.:
assert_that!(6 * 8 - 6).is_equal_to(42);
The variable or expression inside the call of the assert_that!
macro is
repeated in the error message when an assertion fails. For example, the
assertion:
assert_that!(6 * 8 - 5).is_equal_to(42);
will print the error message:
assertion failed: expected 6 * 8 - 5 is equal to 42
but was: 43
expected: 42
By default, the differences between the expected value and the actual value
are highlighted using colors. See the colored
module for more
information on “colored diffs”.
§Examples
§Basic assertions
use asserting::prelude::*;
assert_that!(3 + 5).is_equal_to(8);
assert_that!(69).is_not_equal_to(42);
assert_that!(5).is_greater_than(3);
assert_that!(42).is_at_most(99);
assert_that!(-0.57).is_in_range(-1.0..=1.0);
assert_that!('M').is_in_range('A'..='Z');
assert_that!('M').is_not_in_range('a'..='z');
let subject = "anim proident eiusmod sint".to_string();
assert_that!(subject).contains("eiusmod");
let subject = Some("consectetur veniam at nulla".to_string());
assert_that!(subject).has_value("consectetur veniam at nulla");
let subject: Result<i8, String> = Ok(42);
assert_that!(subject).has_value(42);
let subject: Option<f64> = None;
assert_that!(subject).is_none();
let subject: Result<(), String> = Err("labore qui eu illum".to_string());
assert_that!(subject).has_error("labore qui eu illum");
let subject = vec![1, 3, 5, 7, 9, 11];
assert_that!(subject).contains_exactly([1, 3, 5, 7, 9, 11]);
§Chaining assertions on the same subject
use asserting::prelude::*;
assert_that!("commodo nobis cum duis")
.starts_with("commodo")
.ends_with(" duis")
.has_length(22);
assert_that!(vec![1, 19, 1, 29, 5, 5, 7, 23, 17, 11, 3, 23, 13, 1])
.contains_all_of([1, 11, 13, 17, 19])
.contains_only([1, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31, 37, 43]);
§Asserting each item of a collection or iterator
use asserting::prelude::*;
let numbers = [2, 4, 6, 8, 10];
assert_that!(numbers).each_item(|e|
e.is_greater_than(1)
.is_at_most(10)
);
For more details see Spec::each_item()
.
§Soft assertions
use asserting::prelude::*;
verify_that!("the answer to all important questions is 42")
.contains("unimportant")
.has_at_most_length(41)
.soft_panic();
executes both assertions and prints the messages of both failing assertions in the panic message:
assertion failed: expected subject to contain "unimportant"
but was: "the answer to all important questions is 42"
expected: "unimportant"
assertion failed: expected subject has at most a length of 41
but was: 43
expected: <= 41
For more details see Spec::soft_panic()
.
§Asserting custom types
We can extract a property of a custom type and assert its value:
struct MyStruct {
important_property: String,
other_property: f64,
}
let some_thing = MyStruct {
important_property: "imperdiet aliqua zzril eiusmod".into(),
other_property: 99.9,
};
assert_that!(some_thing).extracting(|s| s.important_property)
.is_equal_to("imperdiet aliqua zzril eiusmod");
Or we can map a custom type that does not implement a required trait to some supported type, e.g., a tuple in this example:
struct Point {
x: i64,
y: i64,
}
let target = Point { x: 12, y: -64 };
assert_that!(target).mapping(|s| (s.x, s.y)).is_equal_to((12, -64));
§Predicate as custom assertion
We can use any predicate function for a custom assertion:
fn is_odd(value: &i32) -> bool {
value & 1 == 1
}
assert_that!(37).satisfies_with_message("expected my number to be odd", is_odd);
§Assert that some code panics or does not panic
Requires crate feature panic
.
use asserting::prelude::*;
fn divide(a: i32, b: i32) -> i32 {
a / b
}
assert_that_code!(|| { divide(7, 0); }).panics();
assert_that_code!(|| { divide(7, 0); })
.panics_with_message("attempt to divide by zero");
assert_that_code!(|| { divide(7, 3); }).does_not_panic();
§The assert_that
and verify_that
functions and macros
Assertions can be written in two ways. The standard way that panics when an assertion fails or the alternative way that collects failures from failed assertions which can be read later.
To call assertion functions on a subject, it is wrapped into the Spec
struct. This can be done by calling one of the functions:
assert_that
- wraps the subject into aSpec
that panics if an assertion failsverify_that
- wraps the subject into aSpec
that collects failures from assertions, which can be read later.assert_that_code
- wraps a closure into aSpec
for asserting whether the code in the closure panics or does not panic. It panics if an assertion fails.verify_that_code
- wraps a closure into aSpec
for asserting whether the code in the closure panics or does not panic. It collects failures from assertions, which can be read later.
The Spec
can hold additional information about the subject, such as the
expression we are asserting, the code location of the assert statement and
an optional description of what we are going to assert. These attributes are
all optional and must be set explicitly by the user.
For convenience, a set of macros with the same names as the functions above is provided which set the expression and the code location for the user.
assert_that!
- calls theassert_that
function and sets the expression inside the macro call as the expression in theSpec
as well as the location of the macro call as the code location.verify_that!
- calls theverify_that
function and sets the expression inside the macro call as the expression in theSpec
as well as the location of the macro call as the code location.assert_that_code!
- calls theassert_that_code
function and sets the expression inside the macro call as the expression in theSpec
as well as the location of the macro call as the code location.verify_that_code!
- calls theverify_that_code
function and sets the expression inside the macro call as the expression in theSpec
as well as the location of the macro call as the code location.
For example, calling the macro assert_that!
like so:
assert_that!(7 * 6).is_equal_to(42);
is equivalent to calling the function assert_that
and then calling
the methods named()
and located_at()
on the returned Spec
,
like so:
assert_that(7 * 6)
.named("7 * 6")
.located_at(
Location {
file: file!(),
line: line!(),
column: column!(),
}
).is_equal_to(42);
When using the verfiy_*
variants of the macros or functions for each
failing assertion, a failure of type AssertFailure
is added to the
Spec
. We can read the failures collected by calling the failures()
method, like so:
let failures = verify_that!(7 * 5).is_equal_to(42).failures();
assert_that!(failures).has_length(1);
or to get a list of formatted failure messages, we can call the
display_failures()
method, like so:
let failures = verify_that!(7 * 5).is_equal_to(42).display_failures();
assert_that!(failures).contains_exactly([
r"assertion failed: expected 7 * 5 is equal to 42
but was: 35
expected: 42
"
]);
§Custom assertions
asserting
provides 4 ways to do custom assertions:
- Predicate functions as custom assertions used with the
Spec::satisfies()
method - Property base assertions for any type that implements a property trait
- Custom expectations used with the
Spec::expecting()
method - Custom assertions methods
How to use predicate functions as custom assertions is described on the
Spec::satisfies()
method and in the Examples
chapter above. The other 3 ways are described in the following subchapters.
§Property-based assertions
Some assertions provided by asserting
are so-called property-based
assertions. They are implemented for all types that implement a related
property trait.
For example, the has_length()
assertion is implemented for all types that
implement the LengthProperty
.
If we want to provide the has_length()
assertion for a custom type, we
simply need to implement the LengthProperty
trait for this type.
Let’s assume we have a custom struct PathWay
and we implement the
LengthProperty
for PathWay
:
use asserting::properties::LengthProperty;
#[derive(Debug)]
struct PathWay {
len: usize
}
impl LengthProperty for PathWay {
fn length_property(&self) -> usize {
self.len
}
}
Then we can assert the length of a PathWay
using the has_length()
assertion:
use asserting::prelude::*;
let some_path = PathWay { len: 27 };
assert_that!(some_path).has_length(27);
Browse the properties
module to see which property traits are available.
§Writing custom expectations
A custom expectation is any type that implements the Expectation
trait.
For example, let’s assume we have a custom type Either
and want to write
an expectation that verifies that a value of type Either
is a left value.
use asserting::spec::{DiffFormat, Expectation, Expression, Unknown};
use std::fmt::Debug;
#[derive(Debug)]
enum Either<L, R> {
Left(L),
Right(R),
}
struct IsLeft;
impl<L, R> Expectation<Either<L, R>> for IsLeft
where
L: Debug,
R: Debug,
{
fn test(&mut self, subject: &Either<L, R>) -> bool {
match subject {
Either::Left(_) => true,
_ => false,
}
}
fn message(&self, expression: &Expression<'_>, actual: &Either<L, R>, _format: &DiffFormat) -> String {
format!(
"expected {expression} is {:?}\n but was: {actual:?}\n expected: {:?}",
Either::Left::<_, Unknown>(Unknown),
Either::Left::<_, Unknown>(Unknown),
)
}
}
We can now use the expectation IsLeft
with the Spec::expecting()
method:
use asserting::prelude::*;
let subject: Either<String, i64> = Either::Left("left value".to_string());
assert_that!(subject).expecting(IsLeft);
§Providing a custom assertion method
In the previous chapter, we implemented a custom expectation which can be
used with the Spec::expecting()
method. But this way is not very
expressive.
Additionally, we can implement a custom assertion method via an extension trait.
use asserting::spec::{FailingStrategy, Spec};
use std::fmt::Debug;
pub trait AssertEither {
fn is_left(self) -> Self;
}
impl<L, R, Q> AssertEither for Spec<'_, Either<L, R>, Q>
where
L: Debug,
R: Debug,
Q: FailingStrategy,
{
fn is_left(self) -> Self {
self.expecting(IsLeft)
}
}
Now we can use the assertion method is_left()
for asserting whether a
subject of type Either
is a left value.
use asserting::prelude::*;
let subject: Either<String, i64> = Either::Left("left value".to_string());
assert_that!(subject).is_left();
Modules§
- assertions
- Definitions of the assertions that are provided by this crate.
- colored
- Functions for highlighting differences between expected and actual values for failed assertions.
- expectations
- Definitions of the expectations that are provided by this crate.
- prelude
- Re-export of all types, traits, functions and macros that are needed to write assertions in tests.
- properties
- Definitions of “properties” that are used by implementations of certain assertions.
- spec
- This is the core of the
asserting
crate.
Macros§
- assert_
that - Starts an assertion for the given subject or expression in the
PanicOnFail
mode. - assert_
that_ code panic
- Starts an assertion for some piece of code in the
PanicOnFail
mode. - verify_
that - Starts an assertion for the given subject or expression in the
CollectFailures
mode. - verify_
that_ code panic
- Starts an assertion for some piece of code in the
CollectFailures
mode.