fluent_asserter/lib.rs
1//! A library to write test assertions with a fluent interface. Writing clean tests is as important as writing clean production code.
2//! This library contains test asserters for many kind of Rust types to produce clean assertions in our automated tests.
3//! It also helps to enhance the Test-Driven Development (TDD) experience, resulting in clean, ergonomic and maintainable tests.
4//!
5//! ## Usage
6//!
7//! Add the dependency to your `Cargo.toml`:
8//!
9//! ```toml
10//! [dependencies]
11//! fluent-asserter = "0.1.9"
12//! ```
13//!
14//! Then import the asserters via the prelude
15//! ```rust
16//! use fluent_asserter::prelude::*;
17//! ```
18//!
19//! Now you should be able to write test assertions with a fluent syntax in your tests.
20//!
21//! ## String and string slice assertions
22//! You can write string assertions for both String and str slices
23//! ```rust
24//!#[test]
25//!fn string_assertions() {
26//! assert_that!("Life tastes great!").is_equal_to("Life tastes great!");
27//! assert_that!("Life tastes great!").contains("great");
28//! assert_that!("Life tastes great!").starts_with("Life");
29//! assert_that!("Life tastes great!").ends_with("!");
30//! assert_that!("Life tastes great!").is_not_empty();
31//! assert_that!("Life tastes great!").has_length(18);
32//! assert_that!("Life tastes great!").contains_any(&["Life", "awesome"]);
33//! assert_that!("Life tastes great!").contains_all(&["Life", "tastes", "great!"]);
34//!}
35//! ```
36//!
37//! ## Number assertions
38//!
39//! ```rust
40//!#[test]
41//!fn number_assertions() {
42//! assert_that!(21).is_equal_to(21);
43//! assert_that!(21).is_smaller_than(22);
44//! assert_that!(21).is_smaller_than_or_equal_to(21);
45//! assert_that!(21).is_greater_than(20);
46//! assert_that!(21).is_in_range(21,31);
47//! assert_that!(21).is_not_in_range(10,20);
48//! assert_that!(3.14159).is_approx_equal(3.142, 0.001);
49//!}
50//! ```
51//!
52//! ## Boolean assertions
53//!
54//! ```rust
55//!#[test]
56//!fn boolean_assertions() {
57//! assert_that!(true).is_true();
58//! assert_that!(false).is_false();
59//!}
60//! ```
61//!
62//! ## Panic assertions
63//!
64//! ```rust
65//! #[test]
66//! fn panic_assertions() {
67//! assert_that_code!(|| panic!("An error occurred!"))
68//! .panics()
69//! .with_message("An error occurred!");
70//!
71//! assert_that_code!(|| println!("Life tastes great!")).does_not_panic();
72//! }
73//! ```
74//!
75//! ## Iterator assertions
76//!
77//! ```rust
78//! #[test]
79//! fn iterator_assertions() {
80//! assert_that!(vec!["tasty", "delicious", "lovely"]).is_equal_to(vec!["tasty", "delicious", "lovely"]);
81//! assert_that!(vec!["tasty", "delicious", "lovely"]).contains("delicious");
82//! assert_that!(vec!["tasty", "delicious", "lovely"]).contains_all(&["tasty", "delicious", "lovely"]);
83//! assert_that!(vec!["tasty", "delicious", "lovely"]).has_count(3);
84//! assert_that!(vec!["tasty", "delicious", "lovely"]).does_not_contain_any(&["awesome", "amazing"]);
85//! assert_that!(vec!["tasty", "delicious", "lovely"]).is_not_empty();
86//! }
87//! ```
88//!
89//! ## Iterator assertion for structs
90//!
91//! ```rust
92//! #[derive(Clone)]
93//! struct Person {
94//! name: String,
95//! age: i32,
96//! }
97//!
98//! #[test]
99//! fn iterator_assertion_for_struct() {
100//! let people: Vec<Person> = vec![
101//! Person {
102//! name: String::from("Daniel"),
103//! age: 32,
104//! },
105//! Person {
106//! name: String::from("Jimmy"),
107//! age: 45,
108//! },
109//! ];
110//!
111//! assert_that!(people).satisfies_respectively(with_asserters!(
112//! |person1: &Person| {
113//! assert_that!(&person1.name).is_equal_to(&String::from("Daniel"));
114//! assert_that!(&person1.age).is_equal_to(&32);
115//! },
116//! |person2: &Person| {
117//! assert_that!(&person2.name).is_equal_to(&String::from("Jimmy"));
118//! assert_that!(&person2.age).is_equal_to(&45);
119//! }
120//! ));
121//! }
122//! ```
123//!
124//! ## Hashmap assertions
125//!
126//!```rust
127//!#[test]
128//!fn hashmap_assertions() {
129//! let mut hash_map = HashMap::<String, String>::new();
130//! assert_that!(&hash_map).is_empty();
131//!
132//! hash_map.insert(String::from("key"), String::from("value"));
133//! assert_that!(&hash_map).has_length(1);
134//! assert_that!(&hash_map).is_not_empty();
135//! assert_that!(&hash_map).contains_key(&String::from("key"));
136//! assert_that!(&hash_map).does_not_contain_key(String::from("key2"));
137//!}
138//!```
139//!
140//!
141//!## Option assertions
142//!
143//!```rust
144//!#[test]
145//!fn option_assertions() {
146//! let option = Option::Some("Winner!");
147//! assert_that!(option).is_some();
148//! assert_that!(option).is_some_with_value("Winner!");
149//!
150//! let none = Option::<i32>::None;
151//! assert_that!(none).is_none();
152//!}
153//!```
154//!
155//!## Result assertions
156//!
157//!```rust
158//!#[test]
159//!pub fn result_assertions() {
160//! let result : Result<i32,i32> = Ok(3);
161//! assert_that!(&result).is_ok();
162//! assert_that!(&result).is_ok_with_value(3);
163//!
164//! let error : Result<i32,String> = Err(String::from("error message"));
165//! assert_that!(&error).is_error();
166//! assert_that!(&error).is_error_with_value(String::from("error message"));
167//!}
168//!```
169
170//! ## Clear and concise error messages
171//!
172//! In case of a failing assertion, the error message is clear and on the point, containing all the information relating to the domain subject.
173//!
174//! ```rust
175//! #[test]
176//! fn test() {
177//! let string_variable = String::from("Hello Rust!");
178//!
179//! assert_that!(string_variable).is_equal_to(String::from("Hello C#!"));
180//! }
181//! ```
182//!
183//! This test produces the following assertion error message:
184//!
185//! ```doc
186//! Expected string_variable to be "Hello C#!", but was "Hello Rust!".
187//! ```
188//!
189
190mod boolean_asserter;
191mod hashmap_asserter;
192mod iterator_asserter;
193mod number_approx_asserter;
194mod number_asserter;
195mod option_asserter;
196mod panic_asserter;
197pub mod prelude;
198mod result_asserter;
199mod string_asserter;
200
201use lazy_static::lazy_static;
202use std::borrow::Borrow;
203use std::fmt::Debug;
204use std::panic;
205
206/// Creating fluent assertion for the specified type.
207/// Depending on the specified type, there are different assertion methods available.
208///
209/// # Examples
210/// ```rust
211/// # #[macro_use] extern crate fluent_asserter;use fluent_asserter::*; fn main() {
212/// assert_that!("awesome").is_equal_to("awesome");
213/// assert_that!(3.14).is_smaller_than(3.15);
214/// assert_that!(true).is_true();
215/// # }
216/// ```
217///
218#[macro_export]
219macro_rules! assert_that {
220 ($value:expr) => {
221 create_asserter($value, stringify!($value).to_string())
222 };
223}
224
225/// Creating fluent assertion to check if a closure panics
226///
227/// NOTE: Do not use this and the native #\[should_panic\] attribute at the same time while executing the tests parallel, as it can have non-deterministic behaviour
228/// # Examples
229/// ```rust
230/// # #[macro_use] extern crate fluent_asserter;use fluent_asserter::*; fn main() {
231/// assert_that_code!(|| panic!("An error occurred!"))
232/// .panics()
233/// .with_message("An error occurred!");
234///
235/// assert_that_code!(|| println!("Life tastes great!"))
236/// .does_not_panic();
237/// # }
238/// ```
239#[macro_export]
240macro_rules! assert_that_code {
241 ($value:expr) => {
242 PanicAsserter::new($value) //TODO: only restrict it to pass function, and nothing else
243 };
244}
245
246pub struct Asserter<T> {
247 value: T,
248 name: String,
249}
250
251pub struct PanicAsserter<F, R>
252where
253 F: FnOnce() -> R + panic::UnwindSafe,
254{
255 value: F,
256}
257
258impl<T> Asserter<T>
259where
260 T: Debug + PartialEq,
261{
262 pub fn new(value: T, name: String) -> Asserter<T> {
263 Asserter { value, name }
264 }
265
266 pub fn is_equal_to(&self, expected_value: T) {
267 let expected = expected_value.borrow();
268 if &self.value != expected {
269 let error_msg = format!(
270 "Expected {} to be {:?}, but was {:?}.",
271 self.name, expected, self.value
272 );
273 panic!("{}", error_msg)
274 }
275 }
276
277 pub fn is_not_equal_to(&self, expected_value: T) {
278 let expected = expected_value.borrow();
279 assert_ne!(&self.value, expected);
280 }
281}
282
283pub fn create_asserter<T>(value: T, name: String) -> Asserter<T> {
284 Asserter { value, name }
285}