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}