RXpect, my take on fluent assertions
⚓ Rust 📅 2026-06-16 👤 surdeus 👁️ 1For a while now, I have been working on a library for fluent assertions with rich error messages. There were a few crates available, but most seem to use macros, which often break RustRover's autocomplete, so I wanted something that uses traits and structs and as few macros as possible. RXpect is the result of that, and at the time of writing this, there are zero macros.
use rxpect::expect;
use rxpect::expectations:EqualityExpectations;
expect(entity).to_equal(TestEntity {
id: "bar",
value: 7,
content: "The quick brown fox jumps over the dog",
content_length: 43,
});
It natively handles Result and Option:
expect(result).to_be_ok_and()
.to_equal(1);
expect(value).to_be_some_and()
.to_equal(1);
That is achieved using projections, which is also a general capability:
expect(entity)
.projected_by(|it| it.value)
.to_equal(1);
You can make multiple expectations on a single value and all failures will be reported:
expect(0)
.to_equal(2)
.to_be_greater_than(1);
thread 'main' (57062) panicked at /home/raniz/src/rxpect/rxpect/src/root.rs:53:17:
Expectation failed (expected == actual)
expected: `2`
actual: `0`
Expectation failed (a > b)
a: `0`
b: `1`
Adding your own expectations is done via extension traits and is how all included expectations are implemented. More info in the docs.
I've been using this for my own purposes for about two and a half years, slowly adding new features and reworking things I don't like, and now I feel it's stable enough for others to use.
There will still be some potentially breaking changes, but these should mostly affect you if you're doing advanced stuff where you actually type traits and structs with their generic parameters. Pure test usage with expect(...).something() should hopefully remain unaffected.
Future additions are mostly more expectations, but I also want to add global and local configuration support, custom reason phrases (something like: expect(answer).to_equal(42).because("Fourty-two is the Answer to the Ultimate Question of Life, the Universe, and Everything")), OR-semantics (i.e. only one of a group of expectations need to pass) and some clever way of asserting on enum type (which will likely require macro usage).
I'm making a conscious effort to keep the number of dependencies down. If you disable all default features, there are zero (non-dev) dependencies. However, if you want to make assertions on iterables (the iterable feature), it adds itertools, and if you want rich diffing (the diff feature), it adds colored and similar. Both iterables and diff are enabled by default.
If you want to get started, check out the documentation, particularly the list of included expectations.
The code and issue tracker live on Codeberg.
1 post - 1 participant
🏷️ Rust_feed
