Skip to content

Commit 27bf4ce

Browse files
committed
doc: provide example for custom assertion method that reuses existing assertions
1 parent 1240075 commit 27bf4ce

4 files changed

Lines changed: 144 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,8 @@ required-features = ["colored"]
100100
name = "assertion_function"
101101
path = "examples/assertion_function.rs"
102102
required-features = ["colored"]
103+
104+
[[example]]
105+
name = "custom_assertion_reusing_existing"
106+
path = "examples/custom_assertion_reusing_existing.rs"
107+
required-features = ["colored"]

examples/assertion_function.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! Running the example will print a failed assertion to the console:
99
//!
1010
//! ```console
11-
//! thread 'main' (5388) panicked at examples\assertion_function.rs:64:5:
11+
//! thread 'main' (5388) panicked at examples\assertion_function.rs:107:5:
1212
//! assertion of snake body failed:
1313
//!
1414
//! expected snake.length to be equal to 3
@@ -27,7 +27,13 @@
2727
//! expected: Coord { x: 2, y: 1 }
2828
//! ```
2929
//!
30-
//! [image of colored output in the console](assets/assertion_function.png)
30+
//! [image of colored output in the console](assertion_function.png)
31+
//!
32+
//! Since version 0.13.0 of `asserting` it is possible to write a custom
33+
//! assertion method with reusing the built-in assertions. This is demonstrated
34+
//! in the example
35+
//! [custom_assertion_reusing_existing.rs](custom_assertion_reusing_existing.rs)
36+
//! which uses the same `Snake` struct and same assertions as this example.
3137
3238
// just to prevent some linter warnings
3339
mod fixture;
23.5 KB
Loading
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//! This example demonstrates how to write a custom assertion for a custom
2+
//! struct by reusing existing assertion methods.
3+
//!
4+
//! We define the extension trait `AssertSnake` with the custom assertion method
5+
//! `has_body`. This trait is implemented for `Spec`. The implementation reuses
6+
//! built-in assertions on each field of the snake. In case of failing
7+
//! assertions, the names of the fields are printed with the failure message.
8+
//!
9+
//! The custom assertion method does not fail on the first failure but collects
10+
//! all failures first and then fails, according to the current
11+
//! `FailingStrategy` of the `Spec`.
12+
//!
13+
//! Running the example will print a failed assertion to the console:
14+
//!
15+
//! ```console
16+
//! thread 'main' (28052) panicked at examples\custom_assertion_reusing_existing.rs:122:25:
17+
//! assertion of snake body failed:
18+
//!
19+
//! expected snake.length to be equal to 3
20+
//! but was: 2
21+
//! expected: 3
22+
//!
23+
//! expected snake.body to contain exactly in order [Coord { x: 2, y: 1 }, Coord { x: 1, y: 1 }, Coord { x: 1, y: 2 }]
24+
//! but was: [Coord { x: 2, y: 1 }, Coord { x: 1, y: 2 }, Coord { x: -1, y: 1 }]
25+
//! expected: [Coord { x: 2, y: 1 }, Coord { x: 1, y: 1 }, Coord { x: 1, y: 2 }]
26+
//! missing: [Coord { x: 1, y: 1 }]
27+
//! extra: [Coord { x: -1, y: 1 }]
28+
//! out-of-order: [Coord { x: 1, y: 2 }]
29+
//!
30+
//! expected snake.head to be equal to Coord { x: 2, y: 1 }
31+
//! but was: Coord { x: 3, y: 1 }
32+
//! expected: Coord { x: 2, y: 1 }
33+
//! ```
34+
//!
35+
//! [image of colored output in the console](custom_assertion_reusing_existing.png)
36+
37+
// just to prevent some linter warnings
38+
mod fixture;
39+
40+
use asserting::prelude::*;
41+
use asserting::spec::{FailingStrategy, Spec};
42+
use std::borrow::Borrow;
43+
44+
#[derive(Debug, Clone, Copy, PartialEq)]
45+
struct Coord {
46+
x: i32,
47+
y: i32,
48+
}
49+
50+
struct Snake {
51+
length: usize,
52+
head: Coord,
53+
body: Vec<Coord>,
54+
}
55+
56+
/// Custom assertion methods for `Snake`.
57+
trait AssertSnake {
58+
#[track_caller]
59+
fn has_body(self, expected_body: &[Coord]) -> Self;
60+
}
61+
62+
// we implement the `AssertSnake` trait for a generic `S: Borrow<Snake>` so that
63+
// the assertion method `has_body` can be called on owned and borrowed `Snake`
64+
// instances.
65+
impl<S, R> AssertSnake for Spec<'_, S, R>
66+
where
67+
S: Borrow<Snake>,
68+
R: FailingStrategy,
69+
{
70+
fn has_body(mut self, expected_body: &[Coord]) -> Self {
71+
let actual_body = self.subject().borrow();
72+
// we first collect all failures using the "soft assertion" mode of
73+
// asserting, which is started by using the `verify_that` function.
74+
let mut failures;
75+
failures = verify_that(actual_body)
76+
// `verify_that` does not highlight differences by default, so we
77+
// switch on highlighting using the configured `DiffFormat`
78+
.with_configured_diff_format()
79+
.extracting(|s| s.length)
80+
.named("snake.length")
81+
.is_equal_to(expected_body.len())
82+
.display_failures();
83+
failures.extend(
84+
verify_that(actual_body)
85+
.with_configured_diff_format()
86+
.extracting(|s| &s.body)
87+
.named("snake.body")
88+
.contains_exactly(expected_body)
89+
.display_failures(),
90+
);
91+
failures.extend(
92+
verify_that(actual_body)
93+
.with_configured_diff_format()
94+
.extracting(|s| s.head)
95+
.named("snake.head")
96+
.is_equal_to(expected_body[0])
97+
.display_failures(),
98+
);
99+
// if there are failures, we fail the whole assertion according to the
100+
// current `FailingStrategy`.
101+
if !failures.is_empty() {
102+
self.do_fail_with_message(format!(
103+
"assertion of snake body failed: \n\n{}",
104+
failures.join("\n")
105+
));
106+
}
107+
self
108+
}
109+
}
110+
111+
fn test() {
112+
let snake = Snake {
113+
length: 2,
114+
head: Coord { x: 3, y: 1 },
115+
body: vec![
116+
Coord { x: 2, y: 1 },
117+
Coord { x: 1, y: 2 },
118+
Coord { x: -1, y: 1 },
119+
],
120+
};
121+
122+
assert_that!(snake).has_body(&[
123+
Coord { x: 2, y: 1 },
124+
Coord { x: 1, y: 1 },
125+
Coord { x: 1, y: 2 },
126+
]);
127+
}
128+
129+
fn main() {
130+
test();
131+
}

0 commit comments

Comments
 (0)