Skip to content

Commit a58e13e

Browse files
authored
feat: Support generic types in test_context macro (#44)
- Allow using test_context with generic contexts like MyContext<T>
1 parent 7cab727 commit a58e13e

File tree

3 files changed

+80
-7
lines changed

3 files changed

+80
-7
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,42 @@ fn test_works(ctx: &mut MyContext) {
3131
}
3232
```
3333

34+
with generic types, you can use same type with different values
35+
36+
```rust
37+
use test_context::{test_context, TestContext};
38+
use std::marker::PhantomData;
39+
40+
struct MyGenericContext<T> {
41+
value: u32,
42+
_marker: PhantomData<T>,
43+
}
44+
45+
impl TestContext for MyGenericContext<String> {
46+
fn setup() -> MyGenericContext<String> {
47+
MyGenericContext { value: 1, _marker: PhantomData }
48+
}
49+
}
50+
51+
#[test_context(MyGenericContext<String>)]
52+
#[test]
53+
fn test_generic_type(ctx: &mut MyGenericContext<String>) {
54+
assert_eq!(ctx.value, 1);
55+
}
56+
57+
impl TestContext for MyGenericContext<u32> {
58+
fn setup() -> MyGenericContext<u32> {
59+
MyGenericContext { value: 2, _marker: PhantomData }
60+
}
61+
}
62+
63+
#[test_context(MyGenericContext<u32>)]
64+
#[test]
65+
fn test_generic_type_u32(ctx: &mut MyGenericContext<u32>) {
66+
assert_eq!(ctx.value, 2);
67+
}
68+
```
69+
3470
Alternatively, you can use `async` functions in your test context by using the
3571
`AsyncTestContext`.
3672

test-context-macros/src/args.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
use syn::{parse::Parse, Ident, Token};
1+
use syn::{parse::Parse, Token, Type};
22

33
pub(crate) struct TestContextArgs {
4-
pub(crate) context_type: Ident,
4+
pub(crate) context_type: Type,
55
pub(crate) skip_teardown: bool,
66
}
77

88
impl Parse for TestContextArgs {
99
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1010
let mut skip_teardown = false;
11-
let mut context_type: Option<Ident> = None;
11+
let mut context_type: Option<Type> = None;
1212

1313
while !input.is_empty() {
1414
let lookahead = input.lookahead1();
@@ -18,10 +18,8 @@ impl Parse for TestContextArgs {
1818
}
1919
let _ = input.parse::<kw::skip_teardown>()?;
2020
skip_teardown = true;
21-
} else if lookahead.peek(Ident) {
22-
if context_type.is_some() {
23-
return Err(input.error("expected only a single type identifier"));
24-
}
21+
} else if context_type.is_none() {
22+
// Parse any valid Rust type, including generic types
2523
context_type = Some(input.parse()?);
2624
} else if lookahead.peek(Token![,]) {
2725
let _ = input.parse::<Token![,]>()?;

test-context/tests/test.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::marker::PhantomData;
2+
13
use test_context::{test_context, AsyncTestContext, TestContext};
24

35
struct Context {
@@ -47,6 +49,43 @@ fn includes_return_value() {
4749
assert_eq!(return_value_func(), 1);
4850
}
4951

52+
struct ContextGeneric<T> {
53+
n: u32,
54+
_marker: PhantomData<T>,
55+
}
56+
57+
struct ContextGenericType1;
58+
impl TestContext for ContextGeneric<ContextGenericType1> {
59+
fn setup() -> Self {
60+
Self {
61+
n: 1,
62+
_marker: PhantomData,
63+
}
64+
}
65+
}
66+
67+
#[test_context(ContextGeneric<ContextGenericType1>)]
68+
#[test]
69+
fn test_generic_type(ctx: &mut ContextGeneric<ContextGenericType1>) {
70+
assert_eq!(ctx.n, 1);
71+
}
72+
73+
struct ContextGenericType2;
74+
impl TestContext for ContextGeneric<ContextGenericType2> {
75+
fn setup() -> Self {
76+
Self {
77+
n: 2,
78+
_marker: PhantomData,
79+
}
80+
}
81+
}
82+
83+
#[test_context(ContextGeneric<ContextGenericType2>)]
84+
#[test]
85+
fn test_generic_type_other(ctx: &mut ContextGeneric<ContextGenericType2>) {
86+
assert_eq!(ctx.n, 2);
87+
}
88+
5089
struct AsyncContext {
5190
n: u32,
5291
}

0 commit comments

Comments
 (0)