Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,3 @@ jobs:
with:
command: clippy
args: --all-targets -- -D warnings

25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

A library for providing custom setup/teardown for Rust tests without needing a test harness.

```rust
```rust ignore
use test_context::{test_context, TestContext};

struct MyContext {
Expand Down Expand Up @@ -77,7 +77,7 @@ 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).

```rust
```rust ignore
#[test_context(MyAsyncContext)]
#[tokio::test]
async fn test_works(ctx: &mut MyAsyncContext) {
Expand All @@ -95,15 +95,15 @@ that runs setup/teardown.

Valid:

```rust
```rust ignore
#[test_context(MyAsyncContext)]
#[tokio::test]
async fn my_test(ctx: &mut MyAsyncContext) {}
```

Invalid:

```rust
```rust ignore
#[tokio::test]
#[test_context(MyAsyncContext)]
async fn my_test(ctx: &mut MyAsyncContext) {}
Expand All @@ -130,7 +130,7 @@ tests annotated with `#[tokio::test]` continue to work as usual without the feat
Also, if you don't care about the teardown execution for a specific test,
you can use the `skip_teardown` keyword on the macro like this:

```rust
```rust ignore
use test_context::{test_context, TestContext};

struct MyContext {}
Expand All @@ -150,7 +150,7 @@ fn test_without_teardown(ctx: &MyContext) {}

If the teardown is ON (default behavior), you can only take a reference to the context, either mutable or immutable, as follows:

```rust
```rust ignore
#[test_context(MyContext)]
#[test]
fn test_with_teardown_using_immutable_ref(ctx: &MyContext) {}
Expand All @@ -162,15 +162,15 @@ fn test_with_teardown_using_mutable_ref(ctx: &mut MyContext) {}

❌The following is invalid:

```rust
```rust ignore
#[test_context(MyContext)]
#[test]
fn test_with_teardown_taking_ownership(ctx: MyContext) {}
```

If the teardown is skipped (as specified in the section above), you can take an immutable ref, mutable ref or full ownership of the context:

```rust
```rust ignore
#[test_context(MyContext, skip_teardown)]
#[test]
fn test_without_teardown(ctx: MyContext) {
Expand All @@ -186,14 +186,13 @@ fn test_without_teardown_taking_a_ref(ctx: &MyContext) {}
fn test_without_teardown_taking_a_mut_ref(ctx: &mut MyContext) {}
```


## ⚠️ Ensure that the context type specified in the macro matches the test function argument type exactly

The error occurs when a context type with an absolute path is mixed with an it's alias.

For example:

```
```rust ignore
mod database {
use test_context::TestContext;

Expand All @@ -207,7 +206,8 @@ mod database {
```

✅The following code will work:
```

```rust ignore
use database::Connection as DbConn;

#[test_context(DbConn)]
Expand All @@ -228,7 +228,8 @@ fn test1(ctx: &mut database::Connection) {
```

❌The following code will not work:
```

```rust ignore
use database::Connection as DbConn;

#[test_context(database::Connection)]
Expand Down
6 changes: 3 additions & 3 deletions test-context-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod macro_args;
mod test_args;

use crate::test_args::{ContextArg, ContextArgMode, TestArg};
use macro_args::TestContextArgs;
use macro_args::MacroArgs;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::ItemFn;
Expand All @@ -29,7 +29,7 @@ use syn::ItemFn;
/// ```
#[proc_macro_attribute]
pub fn test_context(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(attr as TestContextArgs);
let args = syn::parse_macro_input!(attr as MacroArgs);
let input = syn::parse_macro_input!(item as syn::ItemFn);

let (input, context_args) = remove_context_args(input, args.context_type.clone());
Expand Down Expand Up @@ -86,7 +86,7 @@ fn remove_context_args(

fn refactor_input_body(
input: syn::ItemFn,
args: &TestContextArgs,
args: &MacroArgs,
context_arg: ContextArg,
) -> syn::ItemFn {
let context_type = &args.context_type;
Expand Down
10 changes: 7 additions & 3 deletions test-context-macros/src/macro_args.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use syn::{Token, Type, parse::Parse};

pub(crate) struct TestContextArgs {
/// Contains the parsed arguments passed to the macro `#[test_context(..)]`
pub(crate) struct MacroArgs {
/// The context type passed in the macro arguments.
/// It must implement `TestContext` or `AsyncTestContext`
pub(crate) context_type: Type,

pub(crate) skip_teardown: bool,
}

impl Parse for TestContextArgs {
impl Parse for MacroArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut skip_teardown = false;
let mut context_type: Option<Type> = None;
Expand All @@ -28,7 +32,7 @@ impl Parse for TestContextArgs {
}
}

Ok(TestContextArgs {
Ok(MacroArgs {
context_type: context_type
.ok_or(input.error("expected at least one type identifier"))?,
skip_teardown,
Expand Down
126 changes: 1 addition & 125 deletions test-context/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,128 +1,4 @@
//! 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!");
//! }
//! ```
//!
//! # Attribute order
//!
//! Attribute order matters. Always place `#[test_context(...)]` before other test attributes
//! like `#[tokio::test]` or `#[test]`.
//!
//! Why: Rust expands attributes in source order. `#[test_context]` wraps your function and
//! re-attaches the remaining attributes to the wrapper; it must run first so the test attributes
//! apply to the wrapper that performs setup/teardown.
//!
//! Valid:
//! ```ignore
//! #[test_context(MyAsyncContext)]
//! #[tokio::test]
//! async fn my_test(ctx: &mut MyAsyncContext) {}
//! ```
//!
//! Invalid:
//! ```ignore
//! #[tokio::test]
//! #[test_context(MyAsyncContext)]
//! async fn my_test(ctx: &mut MyAsyncContext) {}
//! ```
//!
//! # Skipping the teardown execution
//!
//! Also, if you 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: &mut MyContext) {
//! // Perform any operations that require full ownership of your context
//! }
//! ```
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../README.md"))]

// Reimported to allow for use in the macro.
pub use futures;
Expand Down
3 changes: 3 additions & 0 deletions yamlfmt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
formatter:
type: basic
retain_line_breaks_single: true
Loading