Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arguments destructuring #268

Merged
merged 3 commits into from
Aug 4, 2024
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Changed

- Now it's possible destructuring input values both for cases, values and fixtures. See [#231](https://github.com/la10736/rstest/issues/231) for details

### Add

- Implemented `#[ignore]` attribute to ignore test parameters during fixtures resolution/injection. See [#228](https://github.com/la10736/rstest/issues/228) for details
Expand Down
54 changes: 54 additions & 0 deletions rstest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,22 @@ pub mod timeout;
/// shorter name for argument that represent it in your fixture or test. You can rename the fixture
/// using `#[from(short_name)]` attribute like following example:
///
/// ## Destructuring
///
/// It's possible to destructure the fixture type but, in this case, your're forced to use renaming syntax
/// because it's not possible to guess the fixture name from this syntax:
///
/// ```
/// use rstest::*;
/// #[fixture]
/// fn two_values() -> (u32, u32) { (42, 24) }
///
/// #[rstest]
/// fn the_test(#[from(two_values)] (first, _): (u32, u32)) {
/// assert_eq!(42, first)
/// }
/// ```
///
/// ```
/// use rstest::*;
///
Expand Down Expand Up @@ -568,6 +584,9 @@ pub use rstest_macros::fixture;
/// - return results
/// - marked by `#[should_panic]` attribute
///
/// In the function signature, where you define your tests inputs, you can also destructuring
/// the values like any other rust function.
///
/// If the test function is an [`async` function](#async) `rstest` will run all tests as `async`
/// tests. You can use it just with `async-std` and you should include `attributes` in
/// `async-std`'s features.
Expand Down Expand Up @@ -624,6 +643,20 @@ pub use rstest_macros::fixture;
/// }
/// ```
///
/// The use of `#[from(...)]` attribute is mandatory if you need to destructure the value:
///
/// ```
/// use rstest::*;
///
/// #[fixture]
/// fn tuple() -> (u32, f32) { (42, 42.0) }
///
/// #[rstest]
/// fn the_test(#[from(tuple)] (u, _): (u32, f32)) {
/// assert_eq!(42, u)
/// }
/// ```
///
/// Sometimes is useful to have some parameters in your fixtures but your test would
/// override the fixture's default values in some cases. Like in
/// [fixture partial injection](attr.fixture.html#partial-injection) you use `#[with]`
Expand Down Expand Up @@ -897,6 +930,27 @@ pub use rstest_macros::fixture;
/// }
/// ```
///
/// ## Destructuring inputs
///
/// Both paramtrized case and values can be destructured:
///
/// ```
/// # use rstest::*;
/// struct S {
/// first: u32,
/// second: u32,
/// }
///
/// struct T(i32);
///
/// #[rstest]
/// #[case(S{first: 21, second: 42})]
/// fn some_test(#[case] S{first, second} : S, #[values(T(-1), T(1))] T(t): T) {
/// assert_eq!(1, t * t);
/// assert_eq!(2 * first, second);
/// }
/// ```
///
/// ## Files path as input arguments
///
/// If you need to create a test for each file in a given location you can use
Expand Down
81 changes: 59 additions & 22 deletions rstest/tests/fixture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,9 @@ mod should {
output.stderr.str(),
format!(
r#"
--> {}/src/lib.rs:12:33
--> {name}/src/lib.rs:14:33
|
12 | fn error_cannot_resolve_fixture(no_fixture: u32) {{"#,
name
14 | fn error_cannot_resolve_fixture(no_fixture: u32) {{"#
)
.unindent()
);
Expand All @@ -260,11 +259,10 @@ mod should {
format!(
r#"
error[E0308]: mismatched types
--> {}/src/lib.rs:8:18
|
8 | let a: u32 = "";
"#,
name
--> {name}/src/lib.rs:10:18
|
10 | let a: u32 = "";
"#
)
.unindent()
);
Expand All @@ -277,20 +275,19 @@ mod should {
assert_in!(
output.stderr.str(),
format!(
"
r#"
error[E0308]: mismatched types
--> {}/src/lib.rs:16:29
",
name
--> {name}/src/lib.rs:17:29
"#
)
.unindent()
);

assert_in!(
output.stderr.str(),
"
16 | fn error_fixture_wrong_type(fixture: String) {
| ^^^^^^"
r#"
17 | fn error_fixture_wrong_type(fixture: String) {}
| ^^^^^^"#
.unindent()
);
}
Expand All @@ -304,12 +301,11 @@ mod should {
format!(
"
error: Missed argument: 'not_a_fixture' should be a test function argument.
--> {}/src/lib.rs:19:11
--> {name}/src/lib.rs:19:11
|
19 | #[fixture(not_a_fixture(24))]
| ^^^^^^^^^^^^^
",
name
"
)
.unindent()
);
Expand All @@ -324,15 +320,56 @@ mod should {
format!(
r#"
error: Duplicate argument: 'f' is already defined.
--> {}/src/lib.rs:33:23
--> {name}/src/lib.rs:32:23
|
33 | #[fixture(f("first"), f("second"))]
32 | #[fixture(f("first"), f("second"))]
| ^
"#,
name
"#
)
.unindent()
);
}

#[rstest]
fn on_destruct_implicit_fixture(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();

assert_in!(
output.stderr.str(),
format!(
r#"
error: To destruct a fixture you should provide a path to resolve it by '#[from(...)]' attribute.
--> {name}/src/lib.rs:48:35
|
48 | fn error_destruct_without_resolve(T(a): T) {{}}
| ^^^^^^^
"#
)
.unindent()
);
}

#[rstest]
fn on_destruct_explicit_fixture_without_from(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();

assert_in!(
output.stderr.str(),
format!(
r#"
error: To destruct a fixture you should provide a path to resolve it by '#[from(...)]' attribute.
--> {name}/src/lib.rs:51:57
|
51 | fn error_destruct_without_resolve_also_with(#[with(21)] T(a): T) {{}}
| ^^^^^^^
"#
)
.unindent()
);
assert_eq!(
1,
output.stderr.str().count("51 | fn error_destruct_without")
)
}

#[fixture]
Expand Down
32 changes: 24 additions & 8 deletions rstest/tests/resources/fixture/errors.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
use rstest::*;

#[fixture]
pub fn fixture() -> u32 { 42 }
pub fn fixture() -> u32 {
42
}

#[fixture]
fn error_inner(fixture: u32) {
let a: u32 = "";
}

#[fixture]
fn error_cannot_resolve_fixture(no_fixture: u32) {
}
fn error_cannot_resolve_fixture(no_fixture: u32) {}

#[fixture]
fn error_fixture_wrong_type(fixture: String) {
}
fn error_fixture_wrong_type(fixture: String) {}

#[fixture(not_a_fixture(24))]
fn error_inject_an_invalid_fixture(fixture: String) {
}
fn error_inject_an_invalid_fixture(fixture: String) {}

#[fixture]
fn name() -> &'static str {
Expand All @@ -31,5 +30,22 @@ fn f(name: &str) -> String {
}

#[fixture(f("first"), f("second"))]
fn error_inject_a_fixture_more_than_once(f: String) {
fn error_inject_a_fixture_more_than_once(f: String) {}

struct T(u32);

#[fixture]
fn structed() -> T {
T(42)
}

#[fixture]
fn structed_injectd(fixture: u32) -> T {
T(fixture)
}

#[fixture]
fn error_destruct_without_resolve(T(a): T) {}

#[fixture]
fn error_destruct_without_resolve_also_with(#[with(21)] T(a): T) {}
79 changes: 79 additions & 0 deletions rstest/tests/resources/rstest/destruct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use rstest::*;

struct T {
a: u32,
b: u32,
}

impl T {
fn new(a: u32, b: u32) -> Self {
Self { a, b }
}
}

struct S(u32, u32);

#[fixture]
fn fix() -> T {
T::new(1, 42)
}

#[fixture]
fn named() -> S {
S(1, 42)
}

#[fixture]
fn tuple() -> (u32, u32) {
(1, 42)
}

#[fixture]
fn swap(#[from(fix)] T { a, b }: T) -> T {
T::new(b, a)
}

#[rstest]
fn swapped(#[from(swap)] T { a, b }: T) {
assert_eq!(a, 42);
assert_eq!(b, 1);
}

#[rstest]
#[case::two_times_twenty_one(T::new(2, 21))]
#[case::six_times_seven(T{ a: 6, b: 7 })]
fn cases_destruct(
#[from(fix)] T { a, b }: T,
#[case] T { a: c, b: d }: T,
#[values(T::new(42, 1), T{ a: 3, b: 14})] T { a: e, b: f }: T,
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}

#[rstest]
#[case::two_times_twenty_one(S(2, 21))]
#[case::six_times_seven(S(6, 7))]
fn cases_destruct_named_tuple(
#[from(named)] S(a, b): S,
#[case] S(c, d): S,
#[values(S(42, 1), S(3, 14))] S(e, f): S,
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}

#[rstest]
#[case::two_times_twenty_one((2, 21))]
#[case::six_times_seven((6, 7))]
fn cases_destruct_tuple(
#[from(tuple)] (a, b): (u32, u32),
#[case] (c, d): (u32, u32),
#[values((42, 1), (3, 14))] (e, f): (u32, u32),
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}
6 changes: 6 additions & 0 deletions rstest/tests/resources/rstest/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,9 @@ fn error_timeout_without_duration() {}

#[rstest]
fn error_absolute_path_files(#[files("/tmp/tmp.Q81idVZYAV/*.txt")] path: std::path::PathBuf) {}

struct T(u32, u32);

#[rstest]
#[case(T(3, 4))]
fn wrong_destruct_fixture(T(a, b): T, #[with(42)] T(c, d): T) {}
Loading
Loading