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//! # Attribute order
82//!
83//! Attribute order matters. Always place `#[test_context(...)]` before other test attributes
84//! like `#[tokio::test]` or `#[test]`.
85//!
86//! Why: Rust expands attributes in source order. `#[test_context]` wraps your function and
87//! re-attaches the remaining attributes to the wrapper; it must run first so the test attributes
88//! apply to the wrapper that performs setup/teardown.
89//!
90//! Valid:
91//! ```ignore
92//! #[test_context(MyAsyncContext)]
93//! #[tokio::test]
94//! async fn my_test(ctx: &mut MyAsyncContext) {}
95//! ```
96//!
97//! Invalid:
98//! ```ignore
99//! #[tokio::test]
100//! #[test_context(MyAsyncContext)]
101//! async fn my_test(ctx: &mut MyAsyncContext) {}
102//! ```
103//!
104//! # Skipping the teardown execution
105//!
106//! Also, if you don't care about the teardown execution for a specific test,
107//! you can use the `skip_teardown` keyword on the macro like this:
108//!
109//! ```no_run
110//!  use test_context::{test_context, TestContext};
111//!
112//!  struct MyContext {}
113//!
114//!  impl TestContext for MyContext {
115//!      fn setup() -> MyContext {
116//!          MyContext {}
117//!      }
118//!  }
119//!
120//! #[test_context(MyContext, skip_teardown)]
121//! #[test]
122//! fn test_without_teardown(ctx: &mut MyContext) {
123//!   // Perform any operations that require full ownership of your context
124//! }
125//! ```
126
127// Reimported to allow for use in the macro.
128pub use futures;
129
130pub use test_context_macros::test_context;
131
132/// The trait to implement to get setup/teardown functionality for tests.
133pub trait TestContext
134where
135    Self: Sized,
136{
137    /// Create the context. This is run once before each test that uses the context.
138    fn setup() -> Self;
139
140    /// Perform any additional cleanup of the context besides that already provided by
141    /// normal "drop" semantics.
142    fn teardown(self) {}
143}
144
145/// The trait to implement to get setup/teardown functionality for async tests.
146pub trait AsyncTestContext
147where
148    Self: Sized,
149{
150    /// Create the context. This is run once before each test that uses the context.
151    fn setup() -> impl std::future::Future<Output = Self> + Send;
152
153    /// Perform any additional cleanup of the context besides that already provided by
154    /// normal "drop" semantics.
155    fn teardown(self) -> impl std::future::Future<Output = ()> + Send {
156        async {}
157    }
158}
159
160// Automatically impl TestContext for anything Send that impls AsyncTestContext.
161//
162// A future improvement may be to use feature flags to enable using a specific runtime
163// to run the future synchronously. This is the easiest way to implement it, though, and
164// introduces no new dependencies.
165impl<T> TestContext for T
166where
167    T: AsyncTestContext + Send,
168{
169    fn setup() -> Self {
170        futures::executor::block_on(<T as AsyncTestContext>::setup())
171    }
172
173    fn teardown(self) {
174        futures::executor::block_on(<T as AsyncTestContext>::teardown(self))
175    }
176}