test_context/
lib.rs

1//! A library for providing custom setup/teardown for Rust tests without needing a test harness.
2//!
3//! ```no_run
4//! use test_context::{test_context, TestContext};
5//!
6//! struct MyContext {
7//!     value: String
8//! }
9//!
10//! impl TestContext for MyContext {
11//!     fn setup() -> MyContext {
12//!         MyContext {  value: "Hello, world!".to_string() }
13//!     }
14//!
15//!     fn teardown(self) {
16//!         // Perform any teardown you wish.
17//!     }
18//! }
19//!
20//! #[test_context(MyContext)]
21//! #[test]
22//! fn test_works(ctx: &mut MyContext) {
23//!     assert_eq!(ctx.value, "Hello, world!");
24//! }
25//! ```
26//!
27//! Alternatively, you can use `async` functions in your test context by using the
28//! `AsyncTestContext`.
29//!
30//! ```no_run
31//! use test_context::{test_context, AsyncTestContext};
32//!
33//! struct MyAsyncContext {
34//!     value: String
35//! }
36//!
37//! impl AsyncTestContext for MyAsyncContext {
38//!     async fn setup() -> MyAsyncContext {
39//!         MyAsyncContext { value: "Hello, world!".to_string() }
40//!     }
41//!
42//!     async fn teardown(self) {
43//!         // Perform any teardown you wish.
44//!     }
45//! }
46//!
47//! #[test_context(MyAsyncContext)]
48//! #[test]
49//! fn test_works(ctx: &mut MyAsyncContext) {
50//!     assert_eq!(ctx.value, "Hello, World!");
51//! }
52//! ```
53//!
54//! The `AsyncTestContext` works well with async test wrappers like
55//! [`actix_rt::test`](https://docs.rs/actix-rt/1.1.1/actix_rt/attr.test.html) or
56//! [`tokio::test`](https://docs.rs/tokio/1.0.2/tokio/attr.test.html).
57//!
58//! ```no_run
59//!  use test_context::{test_context, AsyncTestContext};
60//!
61//!  struct MyAsyncContext {
62//!      value: String
63//!  }
64//!
65//!  impl AsyncTestContext for MyAsyncContext {
66//!      async fn setup() -> MyAsyncContext {
67//!          MyAsyncContext { value: "Hello, world!".to_string() }
68//!      }
69//!      async fn teardown(self) {
70//!          // Perform any teardown you wish.
71//!      }
72//!  }
73//!
74//! #[test_context(MyAsyncContext)]
75//! #[tokio::test]
76//! async fn test_async_works(ctx: &mut MyAsyncContext) {
77//!     assert_eq!(ctx.value, "Hello, World!");
78//! }
79//! ```
80//!
81//! # Skipping the teardown execution
82//!
83//! If what you need is to take full __ownership__ of the context and don't care about the
84//! teardown execution for a specific test, you can use the `skip_teardown` keyword on the macro
85//! like this:
86//!
87//! ```no_run
88//!  use test_context::{test_context, TestContext};
89//!
90//!  struct MyContext {}
91//!
92//!  impl TestContext for MyContext {
93//!      fn setup() -> MyContext {
94//!          MyContext {}
95//!      }
96//!  }
97//!
98//! #[test_context(MyContext, skip_teardown)]
99//! #[test]
100//! fn test_without_teardown(ctx: MyContext) {
101//!   // Perform any operations that require full ownership of your context
102//! }
103//! ```
104
105// Reimported to allow for use in the macro.
106pub use futures;
107
108pub use test_context_macros::test_context;
109
110/// The trait to implement to get setup/teardown functionality for tests.
111pub trait TestContext
112where
113    Self: Sized,
114{
115    /// Create the context. This is run once before each test that uses the context.
116    fn setup() -> Self;
117
118    /// Perform any additional cleanup of the context besides that already provided by
119    /// normal "drop" semantics.
120    fn teardown(self) {}
121}
122
123/// The trait to implement to get setup/teardown functionality for async tests.
124pub trait AsyncTestContext
125where
126    Self: Sized,
127{
128    /// Create the context. This is run once before each test that uses the context.
129    fn setup() -> impl std::future::Future<Output = Self> + Send;
130
131    /// Perform any additional cleanup of the context besides that already provided by
132    /// normal "drop" semantics.
133    fn teardown(self) -> impl std::future::Future<Output = ()> + Send {
134        async {}
135    }
136}
137
138// Automatically impl TestContext for anything Send that impls AsyncTestContext.
139//
140// A future improvement may be to use feature flags to enable using a specific runtime
141// to run the future synchronously. This is the easiest way to implement it, though, and
142// introduces no new dependencies.
143impl<T> TestContext for T
144where
145    T: AsyncTestContext + Send,
146{
147    fn setup() -> Self {
148        futures::executor::block_on(<T as AsyncTestContext>::setup())
149    }
150
151    fn teardown(self) {
152        futures::executor::block_on(<T as AsyncTestContext>::teardown(self))
153    }
154}