test_collector/
lib.rs

1//! ## Usage
2//! Do not forget to modify Cargo.toml with.
3//! If you want you can override before_each_test and after_each_test from the TestEnvironment trait. By default these methods are empty.
4//! ```toml
5//! [[test]]
6//! name = "integration"
7//! path = "integration-tests/main.rs"
8//! harness = false
9//! ```
10//!
11//! ``` rust
12//!    use std::future::Future;
13//!    use std::thread;
14//!    use actix_web::{App, HttpResponse, HttpServer, Responder};
15//!    use actix_web::rt::SystemRunner;
16//!    use test_collector_derive::collect_test;
17//!    use test_collector::{log_env_info, TestEnvironment};
18//!    use test_collector::test_runner::TestRunner;
19//!
20//!    struct MockTestEnv {
21//!     system: SystemRunner,
22//!    }
23//!
24//!    impl TestEnvironment for MockTestEnv {
25//!     fn start(self) -> Self {
26//!         log_env_info(format_args!("Starting environment"));
27//!         thread::spawn(move || {
28//!             actix_web::rt::System::new().block_on(async move {
29//!                 HttpServer::new(move || App::new()
30//!                     .service(hello)
31//!                 )
32//!                     .bind("127.0.0.1:9090")?
33//!                     .run()
34//!                     .await
35//!             })
36//!         });
37//!         return self;
38//!     }
39//!
40//!     fn block_on<F: Future>(&self, fut: F) -> F::Output {
41//!         self.system.block_on(fut)
42//!     }
43//!
44//!     fn stop(self) -> Self {
45//!         log_env_info(format_args!("Here You can stop APP, db or any other services"));
46//!         return self;
47//!     }
48//!    }
49//!
50//!    #[actix_web::get("/")]
51//!    async fn hello() -> impl Responder {
52//!     HttpResponse::Ok().body("Hello, world!")
53//!    }
54//!
55//!
56//!    #[test]
57//!    #[should_panic(expected = "Some tests are Failing")]
58//!    fn possible_main() {
59//!     let system = actix_web::rt::System::new();
60//!     let test_runner = TestRunner::new(MockTestEnv{system});
61//!     test_runner.run();
62//!    }
63//!
64//!    #[collect_test]
65//!    pub fn sync_test_failing() {
66//!     println!("Executed sync!");
67//!     assert_eq!(true, false);
68//!    }
69//!
70//!    #[collect_test(async)]
71//!    pub async fn async_test_success() {
72//!     let client = reqwest::Client::builder()
73//!         .build()
74//!         .expect("error during client build");
75//!     let response = client.get("http://localhost:9090/").send().await;
76//!     assert!(response.is_ok());
77//!    }
78//! ```
79
80pub mod test_runner;
81mod logger;
82
83extern crate core;
84
85use std::fmt::Arguments;
86use std::future::Future;
87use std::time::{Duration};
88use crate::logger::log_static_info;
89
90pub trait TestEnvironment {
91    fn start(self) -> Self;
92
93    fn before_each_test(&self) {
94        // do nothing by default
95    }
96
97    fn block_on<F: Future>(&self, fut: F) -> F::Output;
98
99    fn after_each_test(&self) {
100        // do nothing by default
101    }
102
103    fn stop(self) -> Self;
104}
105
106pub struct TestResults {
107    pub success_tests: Vec<TestResult>,
108    pub failed_tests: Vec<TestResult>,
109    pub start_up_duration: Duration,
110    pub tests_duration: Duration,
111    pub stop_duration: Duration,
112}
113
114pub struct TestResult {
115    pub name: String,
116    pub success: bool,
117    pub duration: Duration,
118}
119
120pub fn log_env_info(message: Arguments) {
121    log_static_info(message);
122}
123
124#[cfg(test)]
125mod tests {
126    use std::future::Future;
127    use std::sync::atomic::{AtomicU32, Ordering};
128    use std::thread;
129    use actix_web::{App, HttpResponse, HttpServer, Responder};
130    use actix_web::rt::SystemRunner;
131    use once_cell::sync::OnceCell;
132    use test_collector_derive::collect_test;
133    use Ordering::SeqCst;
134    use std::rc::Rc;
135    use crate::test_runner::TestRunner;
136    use crate::{log_env_info, TestEnvironment};
137
138    struct MockTestEnv {
139        system: SystemRunner,
140        before_each_call: Rc<AtomicU32>,
141        after_each_call: Rc<AtomicU32>,
142    }
143
144    impl TestEnvironment for MockTestEnv {
145        fn start(self) -> Self {
146            log_env_info(format_args!("Starting environment"));
147            log_env_info(format_args!("Setup of environment Finished"));
148            thread::spawn(move || {
149                actix_web::rt::System::new().block_on(async move {
150                    HttpServer::new(move || App::new()
151                        .service(hello)
152                    )
153                        .bind("127.0.0.1:9090")?
154                        .run()
155                        .await
156                })
157            });
158            return self;
159        }
160
161        fn before_each_test(&self) {
162            self.before_each_call.fetch_add(1, SeqCst);
163        }
164
165        fn block_on<F: Future>(&self, fut: F) -> F::Output {
166            self.system.block_on(fut)
167        }
168
169        fn after_each_test(&self) {
170            self.after_each_call.fetch_add(1, SeqCst);
171        }
172
173        fn stop(self) -> Self {
174            log_env_info(format_args!("Teardown started"));
175            log_env_info(format_args!("Here You can stop APP, db or any other services"));
176            log_env_info(format_args!("Teardown finished"));
177            return self;
178        }
179    }
180
181    #[actix_web::get("/")]
182    async fn hello() -> impl Responder {
183        HttpResponse::Ok().body("Hello, world!")
184    }
185
186
187    #[test]
188    #[should_panic(expected = "Some tests are Failing")]
189    fn possible_main() {
190        let system = actix_web::rt::System::new();
191        let test_runner = TestRunner::new(
192            MockTestEnv {
193                system,
194                before_each_call: Rc::new(AtomicU32::new(0)),
195                after_each_call: Rc::new(AtomicU32::new(0)),
196            }
197        );
198
199        test_runner.run();
200    }
201
202    #[test]
203    fn check_before_and_after() {
204        let system = actix_web::rt::System::new();
205        let before_each_call = Rc::new(AtomicU32::new(0));
206        let after_each_call = Rc::new(AtomicU32::new(0));
207        let test_runner = TestRunner::new(
208            MockTestEnv {
209                system,
210                before_each_call: before_each_call.clone(),
211                after_each_call: after_each_call.clone(),
212            }
213        );
214
215        test_runner.run_safe();
216        assert_eq!(before_each_call.fetch_or(0, SeqCst), 4);
217        assert_eq!(after_each_call.fetch_or(0, SeqCst), 4);
218    }
219
220    #[collect_test]
221    pub fn sync_test_failing() {
222        println!("Executed sync!");
223        assert_eq!(true, false);
224    }
225
226    #[collect_test(async)]
227    pub async fn async_test_failing() {
228        let client = reqwest::Client::builder()
229            .build()
230            .expect("error during client build");
231        let response = client.get("http://localhost:9091/").send().await;
232        assert!(response.is_ok());
233    }
234
235    #[collect_test]
236    #[test]
237    pub fn sync_test_success() {
238        println!("Executed sync!");
239        assert_eq!(true, true);
240    }
241
242    #[collect_test(async)]
243    #[actix_web::test]
244    pub async fn async_test_success() {
245        let client = reqwest::Client::builder()
246            .build()
247            .expect("error during client build");
248        let response = client.get("http://localhost:9090/").send().await;
249        assert!(response.is_ok());
250    }
251}