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