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!");
}
struct MyGenericContext<T> {
value: T
}
impl TestContext for MyGenericContext<u32> {
fn setup() -> MyGenericContext<u32> {
MyGenericContext { value: 1 }
}
}
#[test_context(MyGenericContext<u32>)]
#[test]
fn test_generic_type(ctx: &mut MyGenericContext<u32>) {
assert_eq!(ctx.value, 1);
}
Alternatively, you can use async
functions in your test context by using the
AsyncTestContext
.
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)]
fn test_works(ctx: &mut MyAsyncContext) {
assert_eq!(ctx.value, "Hello, World!");
}
The AsyncTestContext
works well with async test wrappers like
actix_rt::test
or
tokio::test
.
#[test_context(MyAsyncContext)]
#[tokio::test]
async fn test_works(ctx: &mut MyAsyncContext) {
assert_eq!(ctx.value, "Hello, World!");
}
Place #[test_context(...)]
before other test attributes like #[tokio::test]
or #[test]
.
Why: Attributes expand in source order. #[test_context]
generates a wrapper and reattaches
the remaining attributes to it. It must run first so the test attribute applies to the wrapper
that runs setup/teardown.
Valid:
#[test_context(MyAsyncContext)]
#[tokio::test]
async fn my_test(ctx: &mut MyAsyncContext) {}
Invalid:
#[tokio::test]
#[test_context(MyAsyncContext)]
async fn my_test(ctx: &mut MyAsyncContext) {}
By default, when you use an AsyncTestContext
in a synchronous test (no #[tokio::test]
),
this crate runs setup
/teardown
using the futures
executor. If your context calls
Tokio-only APIs (e.g., tokio::time::sleep
, timers, or Tokio sockets) during setup/teardown,
enable the optional tokio-runtime
feature so those steps run inside a Tokio runtime:
[dependencies]
test-context = { version = "0.4", features = ["tokio-runtime"] }
With this feature, the crate tries to reuse an existing runtime; if none is present, it creates
an ephemeral current-thread Tokio runtime around setup
and teardown
for sync tests. Async
tests annotated with #[tokio::test]
continue to work as usual without the feature.
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:
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
}
License: MIT