test_suite_rs/
lib.rs

1//! Provides a macro to create a test suite with a setup and teardown function.
2//! Each test block generates a separate test function that will run
3//! setup and teardown functions if provided.
4//!
5//! # Example
6//! ```
7//!
8//! # mod test {
9//! use test_suite_rs::test_suite;
10//!
11//! fn setup() -> (i32, String) {
12//!     (43, "my_string".to_owned())
13//! }
14//!
15//! fn teardown() {}
16//!
17//! test_suite! {
18//!     - name: test_mod
19//!     - setup: setup(i32, String)
20//!     - teardown: teardown
21//!
22//!     test should_return_true(nbr, my_string) {
23//!         assert_eq!(nbr, 43);
24//!         assert_eq!(&my_string, "my_string");
25//!     }
26//!
27//!     test should_return_false {
28//!         assert!(true);
29//!     }
30//! }
31//! # }
32//!```
33//!
34//! Generates the following code (simplified):
35//!
36//!```
37//! # fn setup() -> (i32, String) {
38//! #    (43, "my_string".to_owned())
39//! # }
40//!
41//! # fn teardown() {}
42//!
43//! mod test_mod {
44//! #   use std::assert_eq;
45//!     use super::*;
46//!
47//!     #[test]
48//!     fn should_return_true() {
49//!         let (nbr, my_string) = setup();
50//!
51//!         assert_eq!(nbr, 43);
52//!         assert_eq!(&my_string, "my_string");
53//!         teardown();
54//!     }
55//! }
56
57/// Creates a test suite with a setup and teardown function.
58/// Each test block generates a separate test function that will run
59/// setup and teardown functions if provided.
60///
61/// # Example
62/// ```
63///
64/// # mod test {
65/// use test_suite_rs::test_suite;
66///
67/// fn setup() -> (i32, String) {
68///     (43, "my_string".to_owned())
69/// }
70///
71/// fn teardown() {}
72///
73/// test_suite! {
74///     - name: test_mod
75///     - setup: setup(i32, String)
76///     - teardown: teardown
77///
78///     test should_return_true(nbr, my_string) {
79///         assert_eq!(nbr, 43);
80///         assert_eq!(&my_string, "my_string");
81///     }
82///
83///     test should_return_false {
84///         assert!(true);
85///     }
86/// }
87/// # }
88///```
89///
90/// Generates the following code (simplified):
91///
92///```
93/// # fn setup() -> (i32, String) {
94/// #    (43, "my_string".to_owned())
95/// # }
96///
97/// # fn teardown() {}
98///
99/// mod test_mod {
100/// #   use std::assert_eq;
101///     use super::*;
102///
103///     #[test]
104///     fn should_return_true() {
105///         let (nbr, my_string) = setup();
106///
107///         assert_eq!(nbr, 43);
108///         assert_eq!(&my_string, "my_string");
109///         teardown();
110///     }
111/// }
112#[macro_export]
113macro_rules! test_suite {
114    (
115        - name: $suite_name:ident
116        $(- setup: $setup:ident $(($($arg_type:ty),+))?)?
117        $(- teardown: $teardown:ident)?
118        $(use $top_level_imports:ident::*;)?
119        $(mod $mod_name:ident {
120            $(use $mod_imports:ident::*;)?
121            $(test $test_name:ident$(($($($arg_name:ident)*),+))? $test:block)*
122        })*
123    ) => {
124        mod $suite_name {
125            $(use super::$setup;)?
126            $(use super::$teardown;)?
127            $(use $top_level_imports::*;)?
128
129            fn __internal_test_suite_setup() $($(-> ($($arg_type),*))?)? {
130                $($setup())?
131            }
132
133            fn __internal_test_suite_teardown() {
134                $($teardown();)?
135            }
136
137            $(
138                mod $mod_name {
139                    use super::__internal_test_suite_setup;
140                    use super::__internal_test_suite_teardown;
141                    $(use $mod_imports::*;)?
142
143                    $(
144                        #[test]
145                        fn $test_name() {
146                            // Assign the return value of the setup function to the given names (if specified)
147                            $(let ($($($arg_name)*),*) =)? __internal_test_suite_setup();
148                            // Running test code
149                            let test_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { $test }));
150                            // Running teardown function
151                            let teardown_result = std::panic::catch_unwind(move || { __internal_test_suite_teardown(); });
152                            // Process test results
153                            test_result.unwrap();
154                            teardown_result.unwrap();
155                        }
156                    )*
157                }
158            )*
159        }
160    };
161    (
162        - name: $suite_name:ident
163        $(- setup: $setup:ident $(($($arg_type:ty),+))?)?
164        $(- teardown: $teardown:ident)?
165        $(use $top_level_imports:ident::*;)?
166        $(test $test_name:ident$(($($($arg_name:ident)*),+))? $test:block)*
167    ) => {
168        mod $suite_name {
169            $(use super::$setup;)?
170            $(use super::$teardown;)?
171            $(use $top_level_imports::*;)?
172
173            fn __internal_test_suite_setup() $($(-> ($($arg_type),*))?)? {
174                $($setup())?
175            }
176
177            fn __internal_test_suite_teardown() {
178                $($teardown();)?
179            }
180
181            $(
182                #[test]
183                fn $test_name() {
184                    // Assign the return value of the setup function to the given names (if specified)
185                    $(let ($($($arg_name)*),*) =)? __internal_test_suite_setup();
186                    // Running test code
187                    let test_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { $test }));
188                    // Running teardown function
189                    let teardown_result = std::panic::catch_unwind(move || { __internal_test_suite_teardown(); });
190                    // Process test results
191                    test_result.unwrap();
192                    teardown_result.unwrap();
193                }
194            )*
195        }
196    };
197}
198
199#[cfg(test)]
200mod test {
201    fn setup() -> (i32, &'static str) {
202        (43, "my_string")
203    }
204
205    fn teardown() {}
206
207    fn test_func_in_super() -> bool {
208        true
209    }
210
211    test_suite! {
212        - name: test_suite_full
213        - setup: setup(i32, &'static str)
214        - teardown: teardown
215
216        test creates_the_test(nbr, string) {
217            assert_eq!(string, "my_string");
218            assert_eq!(nbr, 43);
219        }
220    }
221
222    test_suite! {
223        - name: test_suite_no_teardown
224        - setup: setup(i32, &'static str)
225
226        test creates_the_test(nbr, string) {
227            assert_eq!(string, "my_string");
228            assert_eq!(nbr, 43);
229        }
230    }
231
232    test_suite! {
233        - name: test_suite_no_setup
234        - teardown: teardown
235
236        use super::*;
237
238        test creates_the_test {
239            assert!(test_func_in_super());
240        }
241    }
242
243    test_suite! {
244        - name: test_suite_mutability
245        - setup: setup(i32, &'static str)
246
247        test creates_the_test(mut nbr, _string) {
248            assert_eq!(nbr, 43);
249            nbr = 100;
250            assert_eq!(nbr, 100);
251        }
252    }
253
254    test_suite! {
255        - name: test_suite_with_mods
256
257        use super::*;
258
259        mod test_mod {
260            use super::*;
261
262            test test1 {
263                assert!(test_func_in_super())
264            }
265
266            test test2 {
267                assert_eq!(1, 1);
268            }
269        }
270    }
271
272    test_suite! {
273        - name: test_suite_with_mods_and_setup
274        - setup: setup(i32, &'static str)
275
276        use super::*;
277
278        mod test_mod {
279            use super::*;
280
281            test test1 {
282                assert!(test_func_in_super())
283            }
284
285            test test2(nbr, _string) {
286                assert_eq!(nbr, 43);
287            }
288        }
289    }
290}