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
//! A library for providing custom setup/teardown for Rust tests without needing a test harness.
//!
//! ```
//! 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`.
//!
//! ```
//! use test_context::{test_context, AsyncTestContext};
//!
//! struct MyAsyncContext {
//!     value: String
//! }
//!
//! #[async_trait::async_trait]
//! 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)]
//! 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).
//!
//! ```
//! # use test_context::{test_context, AsyncTestContext};
//! # struct MyAsyncContext {
//! #     value: String
//! # }
//! # #[async_trait::async_trait]
//! # 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!");
//! }
//! ```

// 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.
#[async_trait::async_trait]
pub trait AsyncTestContext
where
    Self: Sized,
{
    /// Create the context. This is run once before each test that uses the context.
    async fn setup() -> Self;

    /// Perform any additional cleanup of the context besides that already provided by
    /// normal "drop" semantics.
    async fn teardown(self) {}
}

// 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))
    }
}