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}