mirror of
https://codeberg.org/JasterV/test-context.git
synced 2026-04-26 18:10:06 +00:00
This pull requests introduces changes that make the use of the `test_context` macro more flexible.
Now, if the teardown is not skipped (default behavior), either an `immutable` or a `mutable` reference can be used for the context.
If the teardown is skipped with the `skip_teardown` option, an `immutable`, a `mutable` reference or full ownership can be taken.
So now the following is possible:
```rust
#[test_context(TeardownPanicContext, skip_teardown)]
#[tokio::test]
async fn test_async_skip_teardown(_ctx: &mut TeardownPanicContext) {}
#[test_context(TeardownPanicContext, skip_teardown)]
#[tokio::test]
async fn test_async_skip_teardown_with_immutable_ref(_ctx: &TeardownPanicContext) {}
#[test_context(TeardownPanicContext, skip_teardown)]
#[tokio::test]
async fn test_async_skip_teardown_with_full_ownership(_ctx: TeardownPanicContext) {}
#[test_context(TeardownPanicContext, skip_teardown)]
#[test]
fn test_sync_skip_teardown(_ctx: &mut TeardownPanicContext) {}
#[test_context(TeardownPanicContext, skip_teardown)]
#[test]
fn test_sync_skip_teardown_with_immutable_ref(_ctx: &TeardownPanicContext) {}
#[test_context(TeardownPanicContext, skip_teardown)]
#[test]
fn test_sync_skip_teardown_with_full_ownership(_ctx: TeardownPanicContext) {}
```
254 lines
6.1 KiB
Markdown
254 lines
6.1 KiB
Markdown
[](https://crates.io/crates/test-context)
|
|
[](https://docs.rs/test-context)
|
|

|
|
[](https://github.com/markhildreth/test-context/actions)
|
|
|
|
# test-context
|
|
|
|
A library for providing custom setup/teardown for Rust tests without needing a test harness.
|
|
|
|
```rust
|
|
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`.
|
|
|
|
```rust
|
|
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`](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
|
|
#[test_context(MyAsyncContext)]
|
|
#[tokio::test]
|
|
async fn test_works(ctx: &mut MyAsyncContext) {
|
|
assert_eq!(ctx.value, "Hello, World!");
|
|
}
|
|
```
|
|
|
|
### Attribute order
|
|
|
|
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:
|
|
|
|
```rust
|
|
#[test_context(MyAsyncContext)]
|
|
#[tokio::test]
|
|
async fn my_test(ctx: &mut MyAsyncContext) {}
|
|
```
|
|
|
|
Invalid:
|
|
|
|
```rust
|
|
#[tokio::test]
|
|
#[test_context(MyAsyncContext)]
|
|
async fn my_test(ctx: &mut MyAsyncContext) {}
|
|
```
|
|
|
|
## Using AsyncTestContext in sync tests that require Tokio
|
|
|
|
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:
|
|
|
|
```toml
|
|
[dependencies]
|
|
test-context = { version = "0.5", 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.
|
|
|
|
## 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:
|
|
|
|
```rust
|
|
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) {}
|
|
```
|
|
|
|
## Taking ownership of the context vs taking a reference
|
|
|
|
If the teardown is ON (default behavior), you can only take a reference to the context, either mutable or immutable, as follows:
|
|
|
|
```rust
|
|
#[test_context(MyContext)]
|
|
#[test]
|
|
fn test_with_teardown_using_immutable_ref(ctx: &MyContext) {}
|
|
|
|
#[test_context(MyContext)]
|
|
#[test]
|
|
fn test_with_teardown_using_mutable_ref(ctx: &mut MyContext) {}
|
|
```
|
|
|
|
❌The following is invalid:
|
|
|
|
```rust
|
|
#[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
|
|
#[test_context(MyContext, skip_teardown)]
|
|
#[test]
|
|
fn test_without_teardown(ctx: MyContext) {
|
|
// Perform any operations that require full ownership of your context
|
|
}
|
|
|
|
#[test_context(MyContext, skip_teardown)]
|
|
#[test]
|
|
fn test_without_teardown_taking_a_ref(ctx: &MyContext) {}
|
|
|
|
#[test_context(MyContext, skip_teardown)]
|
|
#[test]
|
|
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:
|
|
|
|
```
|
|
mod database {
|
|
use test_context::TestContext;
|
|
|
|
pub struct Connection;
|
|
|
|
impl TestContext for Connection {
|
|
fn setup() -> Self { Connection }
|
|
fn teardown(self) {...}
|
|
}
|
|
}
|
|
```
|
|
|
|
✅The following code will work:
|
|
```
|
|
use database::Connection as DbConn;
|
|
|
|
#[test_context(DbConn)]
|
|
#[test]
|
|
fn test1(ctx: &mut DbConn) {
|
|
//some test logic
|
|
}
|
|
|
|
// or
|
|
|
|
use database::Connection
|
|
|
|
#[test_context(database::Connection)]
|
|
#[test]
|
|
fn test1(ctx: &mut database::Connection) {
|
|
//some test logic
|
|
}
|
|
```
|
|
|
|
❌The following code will not work:
|
|
```
|
|
use database::Connection as DbConn;
|
|
|
|
#[test_context(database::Connection)]
|
|
#[test]
|
|
fn test1(ctx: &mut DbConn) {
|
|
//some test logic
|
|
}
|
|
|
|
// or
|
|
|
|
use database::Connection as DbConn;
|
|
|
|
#[test_context(DbConn)]
|
|
#[test]
|
|
fn test1(ctx: &mut database::Connection) {
|
|
//some test logic
|
|
}
|
|
```
|
|
|
|
Type mismatches will cause context parsing to fail during either static analysis or compilation.
|
|
|
|
|
|
License: MIT
|