Skip to main content

wae_testing/environment/
mod.rs

1//! 测试环境管理模块
2//!
3//! 提供完整的测试生命周期管理、多服务集成测试支持和容器化测试环境。
4
5use parking_lot::RwLock;
6use std::{
7    future::Future,
8    sync::Arc,
9    time::{Duration, Instant},
10};
11use wae_types::{WaeError, WaeErrorKind, WaeResult as TestingResult};
12
13mod builder;
14mod config;
15mod hooks;
16mod state;
17
18pub use builder::TestEnvBuilder;
19pub use config::{TestEnvConfig, TestServiceConfig};
20pub use hooks::{AsyncTestLifecycleHook, TestLifecycleHook};
21pub use state::TestEnvState;
22
23/// 测试环境管理器
24///
25/// 提供完整的测试生命周期管理,支持同步和异步钩子函数,多服务集成测试,以及容器化测试环境。
26pub struct TestEnv {
27    /// 配置
28    config: TestEnvConfig,
29    /// 状态
30    state: Arc<RwLock<TestEnvState>>,
31    /// 创建时间
32    created_at: Instant,
33    /// 初始化完成时间
34    initialized_at: Arc<RwLock<Option<Instant>>>,
35    /// 同步生命周期钩子
36    lifecycle_hooks: Arc<RwLock<Vec<Box<dyn TestLifecycleHook>>>>,
37    /// 异步生命周期钩子
38    async_lifecycle_hooks: Arc<RwLock<Vec<Box<dyn AsyncTestLifecycleHook>>>>,
39    /// 清理函数列表
40    #[allow(clippy::type_complexity)]
41    cleanup_handlers: Arc<RwLock<Vec<Box<dyn Fn() + Send + Sync>>>>,
42    /// 异步清理函数列表
43    #[allow(clippy::type_complexity)]
44    async_cleanup_handlers: Arc<RwLock<Vec<Box<dyn Fn() -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>>>>,
45    /// 存储的数据
46    storage: Arc<RwLock<std::collections::HashMap<String, Box<dyn std::any::Any + Send + Sync>>>>,
47    /// 测试服务配置
48    services: Arc<RwLock<std::collections::HashMap<String, TestServiceConfig>>>,
49}
50
51impl std::fmt::Debug for TestEnv {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        f.debug_struct("TestEnv")
54            .field("config", &self.config)
55            .field("state", &self.state)
56            .field("created_at", &self.created_at)
57            .field("initialized_at", &self.initialized_at)
58            .field("services", &self.services)
59            .finish()
60    }
61}
62
63impl TestEnv {
64    /// 创建新的测试环境
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use wae_testing::{TestEnv, TestEnvConfig};
70    ///
71    /// let config = TestEnvConfig::default();
72    /// let env = TestEnv::new(config);
73    /// ```
74    pub fn new(config: TestEnvConfig) -> Self {
75        Self {
76            config,
77            state: Arc::new(RwLock::new(TestEnvState::Uninitialized)),
78            created_at: Instant::now(),
79            initialized_at: Arc::new(RwLock::new(None)),
80            lifecycle_hooks: Arc::new(RwLock::new(Vec::new())),
81            async_lifecycle_hooks: Arc::new(RwLock::new(Vec::new())),
82            cleanup_handlers: Arc::new(RwLock::new(Vec::new())),
83            async_cleanup_handlers: Arc::new(RwLock::new(Vec::new())),
84            storage: Arc::new(RwLock::new(std::collections::HashMap::new())),
85            services: Arc::new(RwLock::new(std::collections::HashMap::new())),
86        }
87    }
88
89    /// 创建默认测试环境
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// use wae_testing::TestEnv;
95    ///
96    /// let env = TestEnv::default_env();
97    /// ```
98    pub fn default_env() -> Self {
99        Self::new(TestEnvConfig::default())
100    }
101
102    /// 初始化测试环境
103    ///
104    /// 按顺序执行所有 `before_setup` 钩子、初始化环境、然后执行所有 `after_setup` 钩子。
105    ///
106    /// # Errors
107    ///
108    /// 如果环境已初始化或任何钩子执行失败,将返回 [`WaeError`] 错误。
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use wae_testing::TestEnv;
114    ///
115    /// let env = TestEnv::default_env();
116    /// env.setup().unwrap();
117    /// ```
118    pub fn setup(&self) -> TestingResult<()> {
119        {
120            let mut state = self.state.write();
121            if *state != TestEnvState::Uninitialized {
122                return Err(WaeError::new(WaeErrorKind::EnvironmentError {
123                    reason: "Environment already initialized".to_string(),
124                }));
125            }
126            *state = TestEnvState::Initializing;
127        }
128
129        let result = (|| {
130            for hook in self.lifecycle_hooks.read().iter() {
131                hook.before_setup(self)?;
132            }
133
134            for hook in self.lifecycle_hooks.read().iter() {
135                hook.after_setup(self)?;
136            }
137
138            Ok(())
139        })();
140
141        let mut state = self.state.write();
142        match result {
143            Ok(_) => {
144                *state = TestEnvState::Initialized;
145                *self.initialized_at.write() = Some(Instant::now());
146                Ok(())
147            }
148            Err(e) => {
149                *state = TestEnvState::Uninitialized;
150                Err(e)
151            }
152        }
153    }
154
155    /// 异步初始化测试环境
156    ///
157    /// 异步执行所有生命周期钩子函数,适合需要异步初始化的场景。
158    ///
159    /// # Errors
160    ///
161    /// 如果环境已初始化或任何钩子执行失败,将返回 [`WaeError`] 错误。
162    pub async fn setup_async(&self) -> TestingResult<()> {
163        {
164            let mut state = self.state.write();
165            if *state != TestEnvState::Uninitialized {
166                return Err(WaeError::new(WaeErrorKind::EnvironmentError {
167                    reason: "Environment already initialized".to_string(),
168                }));
169            }
170            *state = TestEnvState::Initializing;
171        }
172
173        let result = (async {
174            for hook in self.lifecycle_hooks.read().iter() {
175                hook.before_setup(self)?;
176            }
177
178            #[allow(clippy::await_holding_lock)]
179            for hook in self.async_lifecycle_hooks.read().iter() {
180                hook.before_setup_async(self).await?;
181            }
182
183            #[allow(clippy::await_holding_lock)]
184            for hook in self.async_lifecycle_hooks.read().iter() {
185                hook.after_setup_async(self).await?;
186            }
187
188            for hook in self.lifecycle_hooks.read().iter() {
189                hook.after_setup(self)?;
190            }
191
192            Ok(())
193        })
194        .await;
195
196        let mut state = self.state.write();
197        match result {
198            Ok(_) => {
199                *state = TestEnvState::Initialized;
200                *self.initialized_at.write() = Some(Instant::now());
201                Ok(())
202            }
203            Err(e) => {
204                *state = TestEnvState::Uninitialized;
205                Err(e)
206            }
207        }
208    }
209
210    /// 清理测试环境
211    ///
212    /// 按顺序执行所有 `before_teardown` 钩子、清理资源、然后执行所有 `after_teardown` 钩子。
213    ///
214    /// # Errors
215    ///
216    /// 如果环境未初始化或任何钩子执行失败,将返回 [`WaeError`] 错误。
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// use wae_testing::TestEnv;
222    ///
223    /// let env = TestEnv::default_env();
224    /// env.setup().unwrap();
225    /// env.teardown().unwrap();
226    /// ```
227    pub fn teardown(&self) -> TestingResult<()> {
228        {
229            let mut state = self.state.write();
230            if *state != TestEnvState::Initialized {
231                return Err(WaeError::new(WaeErrorKind::EnvironmentError {
232                    reason: "Environment not initialized".to_string(),
233                }));
234            }
235            *state = TestEnvState::Destroying;
236        }
237
238        let result = (|| {
239            for hook in self.lifecycle_hooks.read().iter() {
240                hook.before_teardown(self)?;
241            }
242
243            let handlers = self.cleanup_handlers.write();
244            for handler in handlers.iter().rev() {
245                handler();
246            }
247
248            self.storage.write().clear();
249
250            for hook in self.lifecycle_hooks.read().iter() {
251                hook.after_teardown(self)?;
252            }
253
254            Ok(())
255        })();
256
257        let mut state = self.state.write();
258        *state = TestEnvState::Destroyed;
259        result
260    }
261
262    /// 异步清理测试环境
263    ///
264    /// 异步执行所有清理操作,适合需要异步清理的场景。
265    ///
266    /// # Errors
267    ///
268    /// 如果环境未初始化或任何钩子执行失败,将返回 [`WaeError`] 错误。
269    pub async fn teardown_async(&self) -> TestingResult<()> {
270        {
271            let mut state = self.state.write();
272            if *state != TestEnvState::Initialized {
273                return Err(WaeError::new(WaeErrorKind::EnvironmentError {
274                    reason: "Environment not initialized".to_string(),
275                }));
276            }
277            *state = TestEnvState::Destroying;
278        }
279
280        let result = (async {
281            for hook in self.lifecycle_hooks.read().iter() {
282                hook.before_teardown(self)?;
283            }
284
285            #[allow(clippy::await_holding_lock)]
286            for hook in self.async_lifecycle_hooks.read().iter() {
287                hook.before_teardown_async(self).await?;
288            }
289
290            #[allow(clippy::await_holding_lock)]
291            {
292                let handlers = self.async_cleanup_handlers.write();
293                for handler in handlers.iter().rev() {
294                    handler().await;
295                }
296            }
297
298            {
299                let handlers = self.cleanup_handlers.write();
300                for handler in handlers.iter().rev() {
301                    handler();
302                }
303            }
304
305            self.storage.write().clear();
306
307            #[allow(clippy::await_holding_lock)]
308            for hook in self.async_lifecycle_hooks.read().iter() {
309                hook.after_teardown_async(self).await?;
310            }
311
312            for hook in self.lifecycle_hooks.read().iter() {
313                hook.after_teardown(self)?;
314            }
315
316            Ok(())
317        })
318        .await;
319
320        let mut state = self.state.write();
321        *state = TestEnvState::Destroyed;
322        result
323    }
324
325    /// 获取环境状态
326    ///
327    /// # Examples
328    ///
329    /// ```
330    /// use wae_testing::{TestEnv, TestEnvState};
331    ///
332    /// let env = TestEnv::default_env();
333    /// assert_eq!(env.state(), TestEnvState::Uninitialized);
334    /// ```
335    pub fn state(&self) -> TestEnvState {
336        self.state.read().clone()
337    }
338
339    /// 获取环境运行时间
340    ///
341    /// 返回从环境创建到现在经过的时间。
342    pub fn elapsed(&self) -> Duration {
343        self.created_at.elapsed()
344    }
345
346    /// 获取环境初始化后运行的时间
347    ///
348    /// 如果环境尚未初始化,返回 [`None`]。
349    pub fn initialized_elapsed(&self) -> Option<Duration> {
350        self.initialized_at.read().map(|t| t.elapsed())
351    }
352
353    /// 注册同步生命周期钩子
354    ///
355    /// 添加一个同步钩子函数,在测试环境的各个生命周期阶段执行。
356    ///
357    /// # Examples
358    ///
359    /// ```
360    /// use wae_testing::TestEnv;
361    ///
362    /// struct MyHook;
363    ///
364    /// impl wae_testing::TestLifecycleHook for MyHook {
365    ///     fn after_setup(&self, _env: &TestEnv) -> wae_testing::TestingResult<()> {
366    ///         println!("Environment setup complete!");
367    ///         Ok(())
368    ///     }
369    /// }
370    ///
371    /// let env = TestEnv::default_env();
372    /// env.add_lifecycle_hook(MyHook);
373    /// ```
374    pub fn add_lifecycle_hook<H>(&self, hook: H)
375    where
376        H: TestLifecycleHook + 'static,
377    {
378        self.lifecycle_hooks.write().push(Box::new(hook));
379    }
380
381    /// 注册异步生命周期钩子
382    ///
383    /// 添加一个异步钩子函数,在测试环境的各个生命周期阶段异步执行。
384    pub fn add_async_lifecycle_hook<H>(&self, hook: H)
385    where
386        H: AsyncTestLifecycleHook + 'static,
387    {
388        self.async_lifecycle_hooks.write().push(Box::new(hook));
389    }
390
391    /// 注册清理函数
392    ///
393    /// 添加一个同步清理函数,在测试环境清理时执行。清理函数按注册的逆序执行。
394    ///
395    /// # Examples
396    ///
397    /// ```
398    /// use wae_testing::TestEnv;
399    ///
400    /// let env = TestEnv::default_env();
401    /// env.on_cleanup(|| println!("Cleaning up!"));
402    /// ```
403    pub fn on_cleanup<F>(&self, handler: F)
404    where
405        F: Fn() + Send + Sync + 'static,
406    {
407        self.cleanup_handlers.write().push(Box::new(handler));
408    }
409
410    /// 注册异步清理函数
411    ///
412    /// 添加一个异步清理函数,在测试环境清理时异步执行。
413    pub fn on_cleanup_async<F, Fut>(&self, handler: F)
414    where
415        F: Fn() -> Fut + Send + Sync + 'static,
416        Fut: Future<Output = ()> + Send + 'static,
417    {
418        self.async_cleanup_handlers.write().push(Box::new(move || Box::pin(handler())));
419    }
420
421    /// 存储数据
422    ///
423    /// 在测试环境中存储任意类型的数据。
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// use wae_testing::TestEnv;
429    ///
430    /// let env = TestEnv::default_env();
431    /// env.set("test_key", "test_value");
432    /// ```
433    pub fn set<T: 'static + Send + Sync>(&self, key: &str, value: T) {
434        self.storage.write().insert(key.to_string(), Box::new(value));
435    }
436
437    /// 获取数据
438    ///
439    /// 从测试环境中获取之前存储的数据。
440    ///
441    /// # Examples
442    ///
443    /// ```
444    /// use wae_testing::TestEnv;
445    ///
446    /// let env = TestEnv::default_env();
447    /// env.set("test_key", "test_value".to_string());
448    /// let value: Option<String> = env.get("test_key");
449    /// assert_eq!(value, Some("test_value".to_string()));
450    /// ```
451    pub fn get<T: 'static + Clone>(&self, key: &str) -> Option<T> {
452        let storage = self.storage.read();
453        storage.get(key).and_then(|v| v.downcast_ref::<T>().cloned())
454    }
455
456    /// 移除数据
457    ///
458    /// 从测试环境中移除并返回之前存储的数据。
459    pub fn remove<T: 'static>(&self, key: &str) -> Option<T> {
460        let mut storage = self.storage.write();
461        storage.remove(key).and_then(|v| v.downcast::<T>().ok()).map(|v| *v)
462    }
463
464    /// 检查是否存在指定键的数据
465    pub fn has(&self, key: &str) -> bool {
466        self.storage.read().contains_key(key)
467    }
468
469    /// 获取配置
470    ///
471    /// # Examples
472    ///
473    /// ```
474    /// use wae_testing::TestEnv;
475    ///
476    /// let env = TestEnv::default_env();
477    /// let config = env.config();
478    /// assert_eq!(config.name, "test");
479    /// ```
480    pub fn config(&self) -> &TestEnvConfig {
481        &self.config
482    }
483
484    /// 添加测试服务配置
485    ///
486    /// 向测试环境中添加一个服务配置,用于多服务集成测试。
487    pub fn add_service(&self, service_config: TestServiceConfig) {
488        self.services.write().insert(service_config.name.clone(), service_config);
489    }
490
491    /// 获取测试服务配置
492    ///
493    /// 根据服务名称获取服务配置。
494    pub fn get_service(&self, name: &str) -> Option<TestServiceConfig> {
495        self.services.read().get(name).cloned()
496    }
497
498    /// 获取所有启用的服务
499    ///
500    /// 返回所有已启用的服务配置列表。
501    pub fn enabled_services(&self) -> Vec<TestServiceConfig> {
502        self.services.read().values().filter(|s| s.enabled).cloned().collect()
503    }
504
505    /// 使用 fixture 运行测试
506    ///
507    /// 自动管理测试环境的初始化和清理,执行测试函数。
508    ///
509    /// # Errors
510    ///
511    /// 如果环境初始化、测试执行或清理失败,将返回 [`WaeError`] 错误。
512    pub async fn with_fixture<F, R>(&self, fixture: F) -> TestingResult<R>
513    where
514        F: FnOnce() -> TestingResult<R>,
515    {
516        self.setup()?;
517
518        let result = fixture();
519
520        self.teardown()?;
521
522        result
523    }
524
525    /// 运行异步测试
526    ///
527    /// 自动管理测试环境的初始化和清理,执行异步测试函数。
528    ///
529    /// # Errors
530    ///
531    /// 如果环境初始化、测试执行或清理失败,将返回 [`WaeError`] 错误。
532    ///
533    /// # Examples
534    ///
535    /// ```ignore
536    /// use wae_testing::TestEnv;
537    ///
538    /// let env = TestEnv::default_env();
539    /// env.run_test(|| async { Ok(()) }).await.unwrap();
540    /// ```
541    pub async fn run_test<F, Fut>(&self, test: F) -> TestingResult<()>
542    where
543        F: FnOnce() -> Fut,
544        Fut: Future<Output = TestingResult<()>>,
545    {
546        self.setup()?;
547
548        let result = test().await;
549
550        self.teardown()?;
551
552        result
553    }
554
555    /// 运行带异步生命周期的测试
556    ///
557    /// 使用异步初始化和清理运行测试,适合需要异步操作的测试场景。
558    ///
559    /// # Errors
560    ///
561    /// 如果环境初始化、测试执行或清理失败,将返回 [`WaeError`] 错误。
562    pub async fn run_test_async<F, Fut>(&self, test: F) -> TestingResult<()>
563    where
564        F: FnOnce() -> Fut,
565        Fut: Future<Output = TestingResult<()>>,
566    {
567        self.setup_async().await?;
568
569        let result = test().await;
570
571        self.teardown_async().await?;
572
573        result
574    }
575}
576
577impl Drop for TestEnv {
578    fn drop(&mut self) {
579        let state = self.state.read().clone();
580        if state == TestEnvState::Initialized {
581            let _ = self.teardown();
582        }
583    }
584}
585
586/// 创建测试环境
587///
588/// 便捷函数,创建一个默认配置的测试环境。
589///
590/// # Examples
591///
592/// ```
593/// use wae_testing::create_test_env;
594///
595/// let env = create_test_env();
596/// ```
597pub fn create_test_env() -> TestEnv {
598    TestEnv::default_env()
599}
600
601/// 使用配置创建测试环境
602///
603/// 便捷函数,使用指定配置创建测试环境。
604///
605/// # Examples
606///
607/// ```
608/// use wae_testing::{TestEnvConfig, create_test_env_with_config};
609///
610/// let config = TestEnvConfig::default();
611/// let env = create_test_env_with_config(config);
612/// ```
613pub fn create_test_env_with_config(config: TestEnvConfig) -> TestEnv {
614    TestEnv::new(config)
615}