diff --git a/Cargo.toml b/Cargo.toml index 36d616f..ad6f582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,51 +1,11 @@ -[package] -authors = ["Michele d'Amico "] -categories = ["development-tools::testing"] -description = """ -Rust fixture based test framework. It use procedural macro -to implement fixtures and table based tests. -""" -edition = "2018" -exclude = [ - "/playground", - "/rstest_fixtures", -] -homepage = "https://github.com/la10736/rstest" -keywords = ["test", "fixture"] -license = "MIT/Apache-2.0" -name = "rstest" -readme = "README.md" -repository = "https://github.com/la10736/rstest" -version = "0.13.0" - [workspace] exclude = [ "playground", + "rstest_fixtures" ] members = [ + "rstest_macros", + "rstest", "rstest_test", "rstest_reuse", ] - -[lib] -proc-macro = true - -[dependencies] -cfg-if = "1.0.0" -proc-macro2 = "1.0.27" -quote = "1.0.9" -syn = {version = "1.0.72", features = ["full", "parsing", "extra-traits", "visit", "visit-mut"]} - -[dev-dependencies] -actix-rt = "2.2.0" -async-std = {version = "1.9.0", features = ["attributes"]} -lazy_static = "1.4.0" -mytest = {package = "rstest", version = "0.12.0"} -pretty_assertions = "1.0.0" -rstest_reuse = "0.3.0" -rstest_test = "0.5.0" -temp_testdir = "0.2.3" -unindent = "0.1.7" - -[build-dependencies] -rustc_version = "0.4.0" diff --git a/rstest/Cargo.toml b/rstest/Cargo.toml new file mode 100644 index 0000000..cd5d103 --- /dev/null +++ b/rstest/Cargo.toml @@ -0,0 +1,34 @@ +[package] +authors = ["Michele d'Amico "] +categories = ["development-tools::testing"] +description = """ +Rust fixture based test framework. It use procedural macro +to implement fixtures and table based tests. +""" +edition = "2018" +homepage = "https://github.com/la10736/rstest" +keywords = ["test", "fixture"] +license = "MIT/Apache-2.0" +name = "rstest" +readme = "README.md" +repository = "https://github.com/la10736/rstest" +version = "0.13.0" + +[lib] + +[dependencies] +rstest_macros = {version = "0.13.0", path = "../rstest_macros"} + +[dev-dependencies] +actix-rt = "2.2.0" +async-std = {version = "1.9.0", features = ["attributes"]} +lazy_static = "1.4.0" +mytest = { package = "rstest", version = "0.12.0" } +pretty_assertions = "1.0.0" +rstest_reuse = {version = "0.3", path = "../rstest_reuse"} +rstest_test = {version = "0.6", path = "../rstest_test"} +temp_testdir = "0.2.3" +unindent = "0.1.7" + +[build-dependencies] +rustc_version = "0.4.0" diff --git a/rstest/src/lib.rs b/rstest/src/lib.rs new file mode 100644 index 0000000..9badc2c --- /dev/null +++ b/rstest/src/lib.rs @@ -0,0 +1,233 @@ +//! This crate will help you to write simpler tests by leveraging a software testing concept called +//! [test fixtures](https://en.wikipedia.org/wiki/Test_fixture#Software). A fixture is something +//! that you can use in your tests to encapsulate a test's dependencies. +//! +//! The general idea is to have smaller tests that only describe the thing you're testing while you +//! hide the auxiliary utilities your tests make use of somewhere else. +//! For instance, if you have an application that has many tests with users, shopping baskets, and +//! products, you'd have to create a user, a shopping basket, and product every single time in +//! every test which becomes unwieldy quickly. In order to cut down on that repetition, you can +//! instead use fixtures to declare that you need those objects for your function and the fixtures +//! will take care of creating those by themselves. Focus on the important stuff in your tests! +//! +//! In `rstest` a fixture is a function that can return any kind of valid Rust type. This +//! effectively means that your fixtures are not limited by the kind of data they can return. +//! A test can consume an arbitrary number of fixtures at the same time. +//! +//! ## What +//! +//! The `rstest` crate defines the following procedural macros: +//! +//! - [`[rstest]`](macro@rstest): Declare that a test or a group of tests that may take +//! [fixtures](attr.rstest.html#injecting-fixtures), +//! [input table](attr.rstest.html#test-parametrized-cases) or +//! [list of values](attr.rstest.html#values-lists). +//! - [`[fixture]`](macro@fixture): To mark a function as a fixture. +//! +//! ## Why +//! +//! Very often in Rust we write tests like this +//! +//! ``` +//! #[test] +//! fn should_process_two_users() { +//! let mut repository = create_repository(); +//! repository.add("Bob", 21); +//! repository.add("Alice", 22); +//! +//! let processor = string_processor(); +//! processor.send_all(&repository, "Good Morning"); +//! +//! assert_eq!(2, processor.output.find("Good Morning").count()); +//! assert!(processor.output.contains("Bob")); +//! assert!(processor.output.contains("Alice")); +//! } +//! ``` +//! +//! By making use of [`[rstest]`](macro@rstest) we can isolate the dependencies `empty_repository` and +//! `string_processor` by passing them as fixtures: +//! +//! ``` +//! # use rstest::*; +//! #[rstest] +//! fn should_process_two_users(mut empty_repository: impl Repository, +//! string_processor: FakeProcessor) { +//! empty_repository.add("Bob", 21); +//! empty_repository.add("Alice", 22); +//! +//! string_processor.send_all("Good Morning"); +//! +//! assert_eq!(2, string_processor.output.find("Good Morning").count()); +//! assert!(string_processor.output.contains("Bob")); +//! assert!(string_processor.output.contains("Alice")); +//! } +//! ``` +//! +//! ... or if you use `"Alice"` and `"Bob"` in other tests, you can isolate `alice_and_bob` fixture +//! and use it directly: +//! +//! ``` +//! # use rstest::*; +//! # trait Repository { fn add(&mut self, name: &str, age: u8); } +//! # struct Rep; +//! # impl Repository for Rep { fn add(&mut self, name: &str, age: u8) {} } +//! # #[fixture] +//! # fn empty_repository() -> Rep { +//! # Rep +//! # } +//! #[fixture] +//! fn alice_and_bob(mut empty_repository: impl Repository) -> impl Repository { +//! empty_repository.add("Bob", 21); +//! empty_repository.add("Alice", 22); +//! empty_repository +//! } +//! +//! #[rstest] +//! fn should_process_two_users(alice_and_bob: impl Repository, +//! string_processor: FakeProcessor) { +//! string_processor.send_all("Good Morning"); +//! +//! assert_eq!(2, string_processor.output.find("Good Morning").count()); +//! assert!(string_processor.output.contains("Bob")); +//! assert!(string_processor.output.contains("Alice")); +//! } +//! ``` +//! +//! ## Injecting fixtures as function arguments +//! +//! `rstest` functions can receive fixtures by using them as input arguments. +//! A function decorated with [`[rstest]`](attr.rstest.html#injecting-fixtures) +//! will resolve each argument name by call the fixture function. +//! Fixtures should be annotated with the [`[fixture]`](macro@fixture) attribute. +//! +//! Fixtures will be resolved like function calls by following the standard resolution rules. +//! Therefore, an identically named fixture can be use in different context. +//! +//! ``` +//! # use rstest::*; +//! # trait Repository { } +//! # #[derive(Default)] +//! # struct DataSet {} +//! # impl Repository for DataSet { } +//! mod empty_cases { +//! # use rstest::*; +//! # trait Repository { } +//! # #[derive(Default)] +//! # struct DataSet {} +//! # impl Repository for DataSet { } +//! use super::*; +//! +//! #[fixture] +//! fn repository() -> impl Repository { +//! DataSet::default() +//! } +//! +//! #[rstest] +//! fn should_do_nothing(repository: impl Repository) { +//! //.. test impl .. +//! } +//! } +//! +//! mod non_trivial_case { +//! # use rstest::*; +//! # trait Repository { } +//! # #[derive(Default)] +//! # struct DataSet {} +//! # impl Repository for DataSet { } +//! use super::*; +//! +//! #[fixture] +//! fn repository() -> impl Repository { +//! let mut ds = DataSet::default(); +//! // Fill your dataset with interesting case +//! ds +//! } +//! +//! #[rstest] +//! fn should_notify_all_entries(repository: impl Repository) { +//! //.. test impl .. +//! } +//! } +//! +//! ``` +//! +//! Last but not least, fixtures can be injected like we saw in `alice_and_bob` example. +//! +//! ## Creating parametrized tests +//! +//! You can use also [`[rstest]`](attr.rstest.html#test-parametrized-cases) to create +//! simple table-based tests. Let's see the classic Fibonacci example: +//! +//! ``` +//! use rstest::rstest; +//! +//! #[rstest] +//! #[case(0, 0)] +//! #[case(1, 1)] +//! #[case(2, 1)] +//! #[case(3, 2)] +//! #[case(4, 3)] +//! #[case(5, 5)] +//! #[case(6, 8)] +//! fn fibonacci_test(#[case] input: u32,#[case] expected: u32) { +//! assert_eq!(expected, fibonacci(input)) +//! } +//! +//! fn fibonacci(input: u32) -> u32 { +//! match input { +//! 0 => 0, +//! 1 => 1, +//! n => fibonacci(n - 2) + fibonacci(n - 1) +//! } +//! } +//! ``` +//! This will generate a bunch of tests, one for every `#[case(a, b)]`. +//! +//! ## Creating a test for each combinations of given values +//! +//! In some cases you need to test your code for each combinations of some input values. In this +//! cases [`[rstest]`](attr.rstest.html#values-lists) give you the ability to define a list +//! of values (rust expressions) to use for an arguments. +//! +//! ``` +//! # use rstest::rstest; +//! # #[derive(PartialEq, Debug)] +//! # enum State { Init, Start, Processing, Terminated } +//! # #[derive(PartialEq, Debug)] +//! # enum Event { Error, Fatal } +//! # impl State { fn process(self, event: Event) -> Self { self } } +//! +//! #[rstest] +//! fn should_terminate( +//! #[values(State::Init, State::Start, State::Processing)] +//! state: State, +//! #[values(Event::Error, Event::Fatal)] +//! event: Event +//! ) { +//! assert_eq!(State::Terminated, state.process(event)) +//! } +//! ``` +//! +//! This will generate a test for each combination of `state` and `event`. +//! +//! ## Magic Conversion +//! +//! If you need a value where its type implement `FromStr()` trait you +//! can use a literal string to build it. +//! +//! ``` +//! # use rstest::rstest; +//! # use std::net::SocketAddr; +//! #[rstest] +//! #[case("1.2.3.4:8080", 8080)] +//! #[case("127.0.0.1:9000", 9000)] +//! fn check_port(#[case] addr: SocketAddr, #[case] expected: u16) { +//! assert_eq!(expected, addr.port()); +//! } +//! ``` +//! You can use this feature also in value list and in fixture default value. + +#[doc(hidden)] +pub mod magic_conversion; + +pub use rstest_macros::{fixture, rstest}; diff --git a/rstest/src/magic_conversion.rs b/rstest/src/magic_conversion.rs new file mode 100644 index 0000000..4df2301 --- /dev/null +++ b/rstest/src/magic_conversion.rs @@ -0,0 +1,104 @@ +pub struct Magic(pub std::marker::PhantomData); + +pub trait ViaParseDebug<'a, T> { + fn magic_conversion(&self, input: &'a str) -> T; +} + +impl<'a, T> ViaParseDebug<'a, T> for &&Magic +where + T: std::str::FromStr, + T::Err: std::fmt::Debug, +{ + fn magic_conversion(&self, input: &'a str) -> T { + T::from_str(input).unwrap() + } +} + +pub trait ViaParse<'a, T> { + fn magic_conversion(&self, input: &'a str) -> T; +} + +impl<'a, T> ViaParse<'a, T> for &Magic +where + T: std::str::FromStr, +{ + fn magic_conversion(&self, input: &'a str) -> T { + match T::from_str(input) { + Ok(v) => v, + Err(_) => { + panic!( + "Cannot parse '{}' to get {}", + input, + std::any::type_name::() + ); + } + } + } +} + +pub trait ViaIdent<'a, T> { + fn magic_conversion(&self, input: &'a str) -> T; +} + +impl<'a> ViaIdent<'a, &'a str> for &&Magic<&'a str> { + fn magic_conversion(&self, input: &'a str) -> &'a str { + input + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::str::FromStr; + + #[test] + fn should_return_the_same_slice_string() { + assert_eq!( + "something", + (&&&Magic::<&str>(std::marker::PhantomData)).magic_conversion("something") + ); + } + + #[test] + fn should_parse_via_parse_debug() { + assert_eq!( + 42u32, + (&&&Magic::(std::marker::PhantomData)).magic_conversion("42") + ); + } + + #[test] + fn should_parse_via_parse_no_error_debug() { + struct S(String); + struct E; + impl FromStr for S { + type Err = E; + + fn from_str(s: &str) -> Result { + Ok(S(s.to_owned())) + } + } + + assert_eq!( + "some", + (&&&Magic::(std::marker::PhantomData)) + .magic_conversion("some") + .0 + ); + } + + #[test] + #[should_panic(expected = "MyTypeName")] + fn should_show_error() { + struct MyTypeName; + struct E; + impl FromStr for MyTypeName { + type Err = E; + + fn from_str(_s: &str) -> Result { + Err(E) + } + } + (&&&Magic::(std::marker::PhantomData)).magic_conversion(""); + } +} diff --git a/tests/fixture/mod.rs b/rstest/tests/fixture/mod.rs similarity index 97% rename from tests/fixture/mod.rs rename to rstest/tests/fixture/mod.rs index 9521f1a..5cd6141 100644 --- a/tests/fixture/mod.rs +++ b/rstest/tests/fixture/mod.rs @@ -19,7 +19,7 @@ fn run_test(res: &str) -> (std::process::Output, String) { } mod should { - use rstest_test::CountMessageOccurrence; + use rstest_test::{assert_regex, CountMessageOccurrence}; use super::*; @@ -188,7 +188,10 @@ mod should { fn convert_literal_string_for_default_values() { let (output, _) = run_test("default_conversion.rs"); - assert_in!(output.stdout.str(), "Cannot parse 'error' to get MyType"); + assert_regex!( + "Cannot parse 'error' to get [a-z:_0-9]*MyType", + output.stdout.str() + ); TestResults::new() .ok("test_base") diff --git a/tests/integration.rs b/rstest/tests/integration.rs similarity index 100% rename from tests/integration.rs rename to rstest/tests/integration.rs diff --git a/tests/resources/fixture/async_fixture.rs b/rstest/tests/resources/fixture/async_fixture.rs similarity index 100% rename from tests/resources/fixture/async_fixture.rs rename to rstest/tests/resources/fixture/async_fixture.rs diff --git a/tests/resources/fixture/clean_up_default_generics.rs b/rstest/tests/resources/fixture/clean_up_default_generics.rs similarity index 100% rename from tests/resources/fixture/clean_up_default_generics.rs rename to rstest/tests/resources/fixture/clean_up_default_generics.rs diff --git a/tests/resources/fixture/default.rs b/rstest/tests/resources/fixture/default.rs similarity index 100% rename from tests/resources/fixture/default.rs rename to rstest/tests/resources/fixture/default.rs diff --git a/tests/resources/fixture/default_conversion.rs b/rstest/tests/resources/fixture/default_conversion.rs similarity index 100% rename from tests/resources/fixture/default_conversion.rs rename to rstest/tests/resources/fixture/default_conversion.rs diff --git a/tests/resources/fixture/default_in_attrs.rs b/rstest/tests/resources/fixture/default_in_attrs.rs similarity index 100% rename from tests/resources/fixture/default_in_attrs.rs rename to rstest/tests/resources/fixture/default_in_attrs.rs diff --git a/tests/resources/fixture/defined_return_type.rs b/rstest/tests/resources/fixture/defined_return_type.rs similarity index 100% rename from tests/resources/fixture/defined_return_type.rs rename to rstest/tests/resources/fixture/defined_return_type.rs diff --git a/tests/resources/fixture/dyn.rs b/rstest/tests/resources/fixture/dyn.rs similarity index 100% rename from tests/resources/fixture/dyn.rs rename to rstest/tests/resources/fixture/dyn.rs diff --git a/tests/resources/fixture/errors.rs b/rstest/tests/resources/fixture/errors.rs similarity index 100% rename from tests/resources/fixture/errors.rs rename to rstest/tests/resources/fixture/errors.rs diff --git a/tests/resources/fixture/fixture_struct.rs b/rstest/tests/resources/fixture/fixture_struct.rs similarity index 100% rename from tests/resources/fixture/fixture_struct.rs rename to rstest/tests/resources/fixture/fixture_struct.rs diff --git a/tests/resources/fixture/from_other_module.rs b/rstest/tests/resources/fixture/from_other_module.rs similarity index 100% rename from tests/resources/fixture/from_other_module.rs rename to rstest/tests/resources/fixture/from_other_module.rs diff --git a/tests/resources/fixture/impl.rs b/rstest/tests/resources/fixture/impl.rs similarity index 100% rename from tests/resources/fixture/impl.rs rename to rstest/tests/resources/fixture/impl.rs diff --git a/tests/resources/fixture/no_warning.rs b/rstest/tests/resources/fixture/no_warning.rs similarity index 100% rename from tests/resources/fixture/no_warning.rs rename to rstest/tests/resources/fixture/no_warning.rs diff --git a/tests/resources/fixture/once.rs b/rstest/tests/resources/fixture/once.rs similarity index 100% rename from tests/resources/fixture/once.rs rename to rstest/tests/resources/fixture/once.rs diff --git a/tests/resources/fixture/once_defined_type.rs b/rstest/tests/resources/fixture/once_defined_type.rs similarity index 100% rename from tests/resources/fixture/once_defined_type.rs rename to rstest/tests/resources/fixture/once_defined_type.rs diff --git a/tests/resources/fixture/once_no_return.rs b/rstest/tests/resources/fixture/once_no_return.rs similarity index 100% rename from tests/resources/fixture/once_no_return.rs rename to rstest/tests/resources/fixture/once_no_return.rs diff --git a/tests/resources/fixture/partial.rs b/rstest/tests/resources/fixture/partial.rs similarity index 100% rename from tests/resources/fixture/partial.rs rename to rstest/tests/resources/fixture/partial.rs diff --git a/tests/resources/fixture/partial_in_attr.rs b/rstest/tests/resources/fixture/partial_in_attr.rs similarity index 100% rename from tests/resources/fixture/partial_in_attr.rs rename to rstest/tests/resources/fixture/partial_in_attr.rs diff --git a/tests/resources/fixture/rename.rs b/rstest/tests/resources/fixture/rename.rs similarity index 100% rename from tests/resources/fixture/rename.rs rename to rstest/tests/resources/fixture/rename.rs diff --git a/tests/resources/fixture/resolve.rs b/rstest/tests/resources/fixture/resolve.rs similarity index 100% rename from tests/resources/fixture/resolve.rs rename to rstest/tests/resources/fixture/resolve.rs diff --git a/tests/resources/fixture/simple_injection.rs b/rstest/tests/resources/fixture/simple_injection.rs similarity index 100% rename from tests/resources/fixture/simple_injection.rs rename to rstest/tests/resources/fixture/simple_injection.rs diff --git a/tests/resources/rstest/cases/args_with_no_cases.rs b/rstest/tests/resources/rstest/cases/args_with_no_cases.rs similarity index 100% rename from tests/resources/rstest/cases/args_with_no_cases.rs rename to rstest/tests/resources/rstest/cases/args_with_no_cases.rs diff --git a/tests/resources/rstest/cases/async.rs b/rstest/tests/resources/rstest/cases/async.rs similarity index 100% rename from tests/resources/rstest/cases/async.rs rename to rstest/tests/resources/rstest/cases/async.rs diff --git a/tests/resources/rstest/cases/case_attributes.rs b/rstest/tests/resources/rstest/cases/case_attributes.rs similarity index 100% rename from tests/resources/rstest/cases/case_attributes.rs rename to rstest/tests/resources/rstest/cases/case_attributes.rs diff --git a/tests/resources/rstest/cases/case_with_wrong_args.rs b/rstest/tests/resources/rstest/cases/case_with_wrong_args.rs similarity index 100% rename from tests/resources/rstest/cases/case_with_wrong_args.rs rename to rstest/tests/resources/rstest/cases/case_with_wrong_args.rs diff --git a/tests/resources/rstest/cases/description.rs b/rstest/tests/resources/rstest/cases/description.rs similarity index 100% rename from tests/resources/rstest/cases/description.rs rename to rstest/tests/resources/rstest/cases/description.rs diff --git a/tests/resources/rstest/cases/dump_just_one_case.rs b/rstest/tests/resources/rstest/cases/dump_just_one_case.rs similarity index 100% rename from tests/resources/rstest/cases/dump_just_one_case.rs rename to rstest/tests/resources/rstest/cases/dump_just_one_case.rs diff --git a/tests/resources/rstest/cases/inject.rs b/rstest/tests/resources/rstest/cases/inject.rs similarity index 100% rename from tests/resources/rstest/cases/inject.rs rename to rstest/tests/resources/rstest/cases/inject.rs diff --git a/tests/resources/rstest/cases/missed_argument.rs b/rstest/tests/resources/rstest/cases/missed_argument.rs similarity index 100% rename from tests/resources/rstest/cases/missed_argument.rs rename to rstest/tests/resources/rstest/cases/missed_argument.rs diff --git a/tests/resources/rstest/cases/missed_some_arguments.rs b/rstest/tests/resources/rstest/cases/missed_some_arguments.rs similarity index 100% rename from tests/resources/rstest/cases/missed_some_arguments.rs rename to rstest/tests/resources/rstest/cases/missed_some_arguments.rs diff --git a/tests/resources/rstest/cases/partial.rs b/rstest/tests/resources/rstest/cases/partial.rs similarity index 100% rename from tests/resources/rstest/cases/partial.rs rename to rstest/tests/resources/rstest/cases/partial.rs diff --git a/tests/resources/rstest/cases/simple.rs b/rstest/tests/resources/rstest/cases/simple.rs similarity index 100% rename from tests/resources/rstest/cases/simple.rs rename to rstest/tests/resources/rstest/cases/simple.rs diff --git a/tests/resources/rstest/cases/use_attr.rs b/rstest/tests/resources/rstest/cases/use_attr.rs similarity index 100% rename from tests/resources/rstest/cases/use_attr.rs rename to rstest/tests/resources/rstest/cases/use_attr.rs diff --git a/tests/resources/rstest/convert_string_literal.rs b/rstest/tests/resources/rstest/convert_string_literal.rs similarity index 100% rename from tests/resources/rstest/convert_string_literal.rs rename to rstest/tests/resources/rstest/convert_string_literal.rs diff --git a/tests/resources/rstest/dump_debug.rs b/rstest/tests/resources/rstest/dump_debug.rs similarity index 100% rename from tests/resources/rstest/dump_debug.rs rename to rstest/tests/resources/rstest/dump_debug.rs diff --git a/tests/resources/rstest/dump_debug_compact.rs b/rstest/tests/resources/rstest/dump_debug_compact.rs similarity index 100% rename from tests/resources/rstest/dump_debug_compact.rs rename to rstest/tests/resources/rstest/dump_debug_compact.rs diff --git a/tests/resources/rstest/dump_exclude_some_inputs.rs b/rstest/tests/resources/rstest/dump_exclude_some_inputs.rs similarity index 100% rename from tests/resources/rstest/dump_exclude_some_inputs.rs rename to rstest/tests/resources/rstest/dump_exclude_some_inputs.rs diff --git a/tests/resources/rstest/dump_exclude_some_inputs_compact.rs b/rstest/tests/resources/rstest/dump_exclude_some_inputs_compact.rs similarity index 100% rename from tests/resources/rstest/dump_exclude_some_inputs_compact.rs rename to rstest/tests/resources/rstest/dump_exclude_some_inputs_compact.rs diff --git a/tests/resources/rstest/dump_not_debug.rs b/rstest/tests/resources/rstest/dump_not_debug.rs similarity index 100% rename from tests/resources/rstest/dump_not_debug.rs rename to rstest/tests/resources/rstest/dump_not_debug.rs diff --git a/tests/resources/rstest/dump_not_debug_compact.rs b/rstest/tests/resources/rstest/dump_not_debug_compact.rs similarity index 100% rename from tests/resources/rstest/dump_not_debug_compact.rs rename to rstest/tests/resources/rstest/dump_not_debug_compact.rs diff --git a/tests/resources/rstest/errors.rs b/rstest/tests/resources/rstest/errors.rs similarity index 100% rename from tests/resources/rstest/errors.rs rename to rstest/tests/resources/rstest/errors.rs diff --git a/tests/resources/rstest/generic.rs b/rstest/tests/resources/rstest/generic.rs similarity index 100% rename from tests/resources/rstest/generic.rs rename to rstest/tests/resources/rstest/generic.rs diff --git a/tests/resources/rstest/happy_path.rs b/rstest/tests/resources/rstest/happy_path.rs similarity index 100% rename from tests/resources/rstest/happy_path.rs rename to rstest/tests/resources/rstest/happy_path.rs diff --git a/tests/resources/rstest/ignore_args.rs b/rstest/tests/resources/rstest/ignore_args.rs similarity index 100% rename from tests/resources/rstest/ignore_args.rs rename to rstest/tests/resources/rstest/ignore_args.rs diff --git a/tests/resources/rstest/impl_param.rs b/rstest/tests/resources/rstest/impl_param.rs similarity index 100% rename from tests/resources/rstest/impl_param.rs rename to rstest/tests/resources/rstest/impl_param.rs diff --git a/tests/resources/rstest/matrix/async.rs b/rstest/tests/resources/rstest/matrix/async.rs similarity index 100% rename from tests/resources/rstest/matrix/async.rs rename to rstest/tests/resources/rstest/matrix/async.rs diff --git a/tests/resources/rstest/matrix/inject.rs b/rstest/tests/resources/rstest/matrix/inject.rs similarity index 100% rename from tests/resources/rstest/matrix/inject.rs rename to rstest/tests/resources/rstest/matrix/inject.rs diff --git a/tests/resources/rstest/matrix/partial.rs b/rstest/tests/resources/rstest/matrix/partial.rs similarity index 100% rename from tests/resources/rstest/matrix/partial.rs rename to rstest/tests/resources/rstest/matrix/partial.rs diff --git a/tests/resources/rstest/matrix/simple.rs b/rstest/tests/resources/rstest/matrix/simple.rs similarity index 100% rename from tests/resources/rstest/matrix/simple.rs rename to rstest/tests/resources/rstest/matrix/simple.rs diff --git a/tests/resources/rstest/matrix/use_attr.rs b/rstest/tests/resources/rstest/matrix/use_attr.rs similarity index 100% rename from tests/resources/rstest/matrix/use_attr.rs rename to rstest/tests/resources/rstest/matrix/use_attr.rs diff --git a/tests/resources/rstest/mut.rs b/rstest/tests/resources/rstest/mut.rs similarity index 100% rename from tests/resources/rstest/mut.rs rename to rstest/tests/resources/rstest/mut.rs diff --git a/tests/resources/rstest/panic.rs b/rstest/tests/resources/rstest/panic.rs similarity index 100% rename from tests/resources/rstest/panic.rs rename to rstest/tests/resources/rstest/panic.rs diff --git a/tests/resources/rstest/reject_no_item_function.rs b/rstest/tests/resources/rstest/reject_no_item_function.rs similarity index 100% rename from tests/resources/rstest/reject_no_item_function.rs rename to rstest/tests/resources/rstest/reject_no_item_function.rs diff --git a/tests/resources/rstest/remove_underscore.rs b/rstest/tests/resources/rstest/remove_underscore.rs similarity index 100% rename from tests/resources/rstest/remove_underscore.rs rename to rstest/tests/resources/rstest/remove_underscore.rs diff --git a/tests/resources/rstest/rename.rs b/rstest/tests/resources/rstest/rename.rs similarity index 100% rename from tests/resources/rstest/rename.rs rename to rstest/tests/resources/rstest/rename.rs diff --git a/tests/resources/rstest/return_result.rs b/rstest/tests/resources/rstest/return_result.rs similarity index 100% rename from tests/resources/rstest/return_result.rs rename to rstest/tests/resources/rstest/return_result.rs diff --git a/tests/resources/rstest/single/async.rs b/rstest/tests/resources/rstest/single/async.rs similarity index 100% rename from tests/resources/rstest/single/async.rs rename to rstest/tests/resources/rstest/single/async.rs diff --git a/tests/resources/rstest/single/dump_debug.rs b/rstest/tests/resources/rstest/single/dump_debug.rs similarity index 100% rename from tests/resources/rstest/single/dump_debug.rs rename to rstest/tests/resources/rstest/single/dump_debug.rs diff --git a/tests/resources/rstest/single/inject.rs b/rstest/tests/resources/rstest/single/inject.rs similarity index 100% rename from tests/resources/rstest/single/inject.rs rename to rstest/tests/resources/rstest/single/inject.rs diff --git a/tests/resources/rstest/single/partial.rs b/rstest/tests/resources/rstest/single/partial.rs similarity index 100% rename from tests/resources/rstest/single/partial.rs rename to rstest/tests/resources/rstest/single/partial.rs diff --git a/tests/resources/rstest/single/resolve.rs b/rstest/tests/resources/rstest/single/resolve.rs similarity index 100% rename from tests/resources/rstest/single/resolve.rs rename to rstest/tests/resources/rstest/single/resolve.rs diff --git a/tests/resources/rstest/single/simple.rs b/rstest/tests/resources/rstest/single/simple.rs similarity index 100% rename from tests/resources/rstest/single/simple.rs rename to rstest/tests/resources/rstest/single/simple.rs diff --git a/tests/resources/rstest/use_mutable_fixture_in_parametric_argumnts.rs b/rstest/tests/resources/rstest/use_mutable_fixture_in_parametric_argumnts.rs similarity index 100% rename from tests/resources/rstest/use_mutable_fixture_in_parametric_argumnts.rs rename to rstest/tests/resources/rstest/use_mutable_fixture_in_parametric_argumnts.rs diff --git a/tests/rstest/mod.rs b/rstest/tests/rstest/mod.rs similarity index 99% rename from tests/rstest/mod.rs rename to rstest/tests/rstest/mod.rs index efd0291..3b8090a 100644 --- a/tests/rstest/mod.rs +++ b/rstest/tests/rstest/mod.rs @@ -824,7 +824,10 @@ mod matrix { fn convert_string_literal() { let (output, _) = run_test("convert_string_literal.rs"); - assert_in!(output.stdout.str(), "Cannot parse 'error' to get MyType"); + assert_regex!( + "Cannot parse 'error' to get [a-z:_0-9]*MyType", + output.stdout.str() + ); TestResults::new() .ok("cases::case_1") diff --git a/rstest_macros/Cargo.toml b/rstest_macros/Cargo.toml new file mode 100644 index 0000000..eeba919 --- /dev/null +++ b/rstest_macros/Cargo.toml @@ -0,0 +1,36 @@ +[package] +authors = ["Michele d'Amico "] +categories = ["development-tools::testing"] +description = """ +Rust fixture based test framework. It use procedural macro +to implement fixtures and table based tests. +""" +edition = "2018" +homepage = "https://github.com/la10736/rstest" +keywords = ["test", "fixture"] +license = "MIT/Apache-2.0" +name = "rstest_macros" +repository = "https://github.com/la10736/rstest" +version = "0.13.0" + +[lib] +proc-macro = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cfg-if = "1.0.0" +proc-macro2 = "1.0.27" +quote = "1.0.9" +syn = {version = "1.0.72", features = ["full", "parsing", "extra-traits", "visit", "visit-mut"]} + +[dev-dependencies] +rstest = "0.12.0" +pretty_assertions = "1.0.0" +rstest_test = "0.5.0" +rstest_reuse = "0.3.0" +actix-rt = "2.2.0" +async-std = {version = "1.9.0", features = ["attributes"]} + +[build-dependencies] +rustc_version = "0.4.0" diff --git a/build.rs b/rstest_macros/build.rs similarity index 100% rename from build.rs rename to rstest_macros/build.rs diff --git a/src/error.rs b/rstest_macros/src/error.rs similarity index 100% rename from src/error.rs rename to rstest_macros/src/error.rs diff --git a/src/lib.rs b/rstest_macros/src/lib.rs similarity index 78% rename from src/lib.rs rename to rstest_macros/src/lib.rs index fff9f5c..d6cbe63 100644 --- a/src/lib.rs +++ b/rstest_macros/src/lib.rs @@ -1,232 +1,3 @@ -//! This crate will help you to write simpler tests by leveraging a software testing concept called -//! [test fixtures](https://en.wikipedia.org/wiki/Test_fixture#Software). A fixture is something -//! that you can use in your tests to encapsulate a test's dependencies. -//! -//! The general idea is to have smaller tests that only describe the thing you're testing while you -//! hide the auxiliary utilities your tests make use of somewhere else. -//! For instance, if you have an application that has many tests with users, shopping baskets, and -//! products, you'd have to create a user, a shopping basket, and product every single time in -//! every test which becomes unwieldy quickly. In order to cut down on that repetition, you can -//! instead use fixtures to declare that you need those objects for your function and the fixtures -//! will take care of creating those by themselves. Focus on the important stuff in your tests! -//! -//! In `rstest` a fixture is a function that can return any kind of valid Rust type. This -//! effectively means that your fixtures are not limited by the kind of data they can return. -//! A test can consume an arbitrary number of fixtures at the same time. -//! -//! ## What -//! -//! The `rstest` crate defines the following procedural macros: -//! -//! - [`[rstest]`](macro@rstest): Declare that a test or a group of tests that may take -//! [fixtures](attr.rstest.html#injecting-fixtures), -//! [input table](attr.rstest.html#test-parametrized-cases) or -//! [list of values](attr.rstest.html#values-lists). -//! - [`[fixture]`](macro@fixture): To mark a function as a fixture. -//! -//! ## Why -//! -//! Very often in Rust we write tests like this -//! -//! ``` -//! #[test] -//! fn should_process_two_users() { -//! let mut repository = create_repository(); -//! repository.add("Bob", 21); -//! repository.add("Alice", 22); -//! -//! let processor = string_processor(); -//! processor.send_all(&repository, "Good Morning"); -//! -//! assert_eq!(2, processor.output.find("Good Morning").count()); -//! assert!(processor.output.contains("Bob")); -//! assert!(processor.output.contains("Alice")); -//! } -//! ``` -//! -//! By making use of [`[rstest]`](macro@rstest) we can isolate the dependencies `empty_repository` and -//! `string_processor` by passing them as fixtures: -//! -//! ``` -//! # use rstest::*; -//! #[rstest] -//! fn should_process_two_users(mut empty_repository: impl Repository, -//! string_processor: FakeProcessor) { -//! empty_repository.add("Bob", 21); -//! empty_repository.add("Alice", 22); -//! -//! string_processor.send_all("Good Morning"); -//! -//! assert_eq!(2, string_processor.output.find("Good Morning").count()); -//! assert!(string_processor.output.contains("Bob")); -//! assert!(string_processor.output.contains("Alice")); -//! } -//! ``` -//! -//! ... or if you use `"Alice"` and `"Bob"` in other tests, you can isolate `alice_and_bob` fixture -//! and use it directly: -//! -//! ``` -//! # use rstest::*; -//! # trait Repository { fn add(&mut self, name: &str, age: u8); } -//! # struct Rep; -//! # impl Repository for Rep { fn add(&mut self, name: &str, age: u8) {} } -//! # #[fixture] -//! # fn empty_repository() -> Rep { -//! # Rep -//! # } -//! #[fixture] -//! fn alice_and_bob(mut empty_repository: impl Repository) -> impl Repository { -//! empty_repository.add("Bob", 21); -//! empty_repository.add("Alice", 22); -//! empty_repository -//! } -//! -//! #[rstest] -//! fn should_process_two_users(alice_and_bob: impl Repository, -//! string_processor: FakeProcessor) { -//! string_processor.send_all("Good Morning"); -//! -//! assert_eq!(2, string_processor.output.find("Good Morning").count()); -//! assert!(string_processor.output.contains("Bob")); -//! assert!(string_processor.output.contains("Alice")); -//! } -//! ``` -//! -//! ## Injecting fixtures as function arguments -//! -//! `rstest` functions can receive fixtures by using them as input arguments. -//! A function decorated with [`[rstest]`](attr.rstest.html#injecting-fixtures) -//! will resolve each argument name by call the fixture function. -//! Fixtures should be annotated with the [`[fixture]`](macro@fixture) attribute. -//! -//! Fixtures will be resolved like function calls by following the standard resolution rules. -//! Therefore, an identically named fixture can be use in different context. -//! -//! ``` -//! # use rstest::*; -//! # trait Repository { } -//! # #[derive(Default)] -//! # struct DataSet {} -//! # impl Repository for DataSet { } -//! mod empty_cases { -//! # use rstest::*; -//! # trait Repository { } -//! # #[derive(Default)] -//! # struct DataSet {} -//! # impl Repository for DataSet { } -//! use super::*; -//! -//! #[fixture] -//! fn repository() -> impl Repository { -//! DataSet::default() -//! } -//! -//! #[rstest] -//! fn should_do_nothing(repository: impl Repository) { -//! //.. test impl .. -//! } -//! } -//! -//! mod non_trivial_case { -//! # use rstest::*; -//! # trait Repository { } -//! # #[derive(Default)] -//! # struct DataSet {} -//! # impl Repository for DataSet { } -//! use super::*; -//! -//! #[fixture] -//! fn repository() -> impl Repository { -//! let mut ds = DataSet::default(); -//! // Fill your dataset with interesting case -//! ds -//! } -//! -//! #[rstest] -//! fn should_notify_all_entries(repository: impl Repository) { -//! //.. test impl .. -//! } -//! } -//! -//! ``` -//! -//! Last but not least, fixtures can be injected like we saw in `alice_and_bob` example. -//! -//! ## Creating parametrized tests -//! -//! You can use also [`[rstest]`](attr.rstest.html#test-parametrized-cases) to create -//! simple table-based tests. Let's see the classic Fibonacci example: -//! -//! ``` -//! use rstest::rstest; -//! -//! #[rstest] -//! #[case(0, 0)] -//! #[case(1, 1)] -//! #[case(2, 1)] -//! #[case(3, 2)] -//! #[case(4, 3)] -//! #[case(5, 5)] -//! #[case(6, 8)] -//! fn fibonacci_test(#[case] input: u32,#[case] expected: u32) { -//! assert_eq!(expected, fibonacci(input)) -//! } -//! -//! fn fibonacci(input: u32) -> u32 { -//! match input { -//! 0 => 0, -//! 1 => 1, -//! n => fibonacci(n - 2) + fibonacci(n - 1) -//! } -//! } -//! ``` -//! This will generate a bunch of tests, one for every `#[case(a, b)]`. -//! -//! ## Creating a test for each combinations of given values -//! -//! In some cases you need to test your code for each combinations of some input values. In this -//! cases [`[rstest]`](attr.rstest.html#values-lists) give you the ability to define a list -//! of values (rust expressions) to use for an arguments. -//! -//! ``` -//! # use rstest::rstest; -//! # #[derive(PartialEq, Debug)] -//! # enum State { Init, Start, Processing, Terminated } -//! # #[derive(PartialEq, Debug)] -//! # enum Event { Error, Fatal } -//! # impl State { fn process(self, event: Event) -> Self { self } } -//! -//! #[rstest] -//! fn should_terminate( -//! #[values(State::Init, State::Start, State::Processing)] -//! state: State, -//! #[values(Event::Error, Event::Fatal)] -//! event: Event -//! ) { -//! assert_eq!(State::Terminated, state.process(event)) -//! } -//! ``` -//! -//! This will generate a test for each combination of `state` and `event`. -//! -//! ## Magic Conversion -//! -//! If you need a value where its type implement `FromStr()` trait you -//! can use a literal string to build it. -//! -//! ``` -//! # use rstest::rstest; -//! # use std::net::SocketAddr; -//! #[rstest] -//! #[case("1.2.3.4:8080", 8080)] -//! #[case("127.0.0.1:9000", 9000)] -//! fn check_port(#[case] addr: SocketAddr, #[case] expected: u16) { -//! assert_eq!(expected, addr.port()); -//! } -//! ``` -//! You can use this feature also in value list and in fixture default value. - #![cfg_attr(use_proc_macro_diagnostic, feature(proc_macro_diagnostic))] extern crate proc_macro; diff --git a/src/parse/expressions.rs b/rstest_macros/src/parse/expressions.rs similarity index 100% rename from src/parse/expressions.rs rename to rstest_macros/src/parse/expressions.rs diff --git a/src/parse/fixture.rs b/rstest_macros/src/parse/fixture.rs similarity index 99% rename from src/parse/fixture.rs rename to rstest_macros/src/parse/fixture.rs index 8825e65..793e066 100644 --- a/src/parse/fixture.rs +++ b/rstest_macros/src/parse/fixture.rs @@ -325,7 +325,6 @@ mod should { mod parse { use super::{assert_eq, *}; - use mytest::rstest; fn parse_fixture>(fixture_data: S) -> FixtureInfo { parse_meta(fixture_data) diff --git a/src/parse/future.rs b/rstest_macros/src/parse/future.rs similarity index 99% rename from src/parse/future.rs rename to rstest_macros/src/parse/future.rs index 08a6340..6e74f5a 100644 --- a/src/parse/future.rs +++ b/rstest_macros/src/parse/future.rs @@ -104,7 +104,6 @@ impl VisitMut for ReplaceFutureAttribute { mod should { use super::*; use crate::test::{assert_eq, *}; - use mytest::rstest; use rstest_test::assert_in; #[rstest] diff --git a/src/parse/macros.rs b/rstest_macros/src/parse/macros.rs similarity index 100% rename from src/parse/macros.rs rename to rstest_macros/src/parse/macros.rs diff --git a/src/parse/mod.rs b/rstest_macros/src/parse/mod.rs similarity index 100% rename from src/parse/mod.rs rename to rstest_macros/src/parse/mod.rs diff --git a/src/parse/rstest.rs b/rstest_macros/src/parse/rstest.rs similarity index 100% rename from src/parse/rstest.rs rename to rstest_macros/src/parse/rstest.rs diff --git a/src/parse/testcase.rs b/rstest_macros/src/parse/testcase.rs similarity index 100% rename from src/parse/testcase.rs rename to rstest_macros/src/parse/testcase.rs diff --git a/src/parse/vlist.rs b/rstest_macros/src/parse/vlist.rs similarity index 100% rename from src/parse/vlist.rs rename to rstest_macros/src/parse/vlist.rs diff --git a/src/refident.rs b/rstest_macros/src/refident.rs similarity index 100% rename from src/refident.rs rename to rstest_macros/src/refident.rs diff --git a/src/render/fixture.rs b/rstest_macros/src/render/fixture.rs similarity index 100% rename from src/render/fixture.rs rename to rstest_macros/src/render/fixture.rs diff --git a/src/render/inject.rs b/rstest_macros/src/render/inject.rs similarity index 80% rename from src/render/inject.rs rename to rstest_macros/src/render/inject.rs index f17e4ee..da72a0c 100644 --- a/src/render/inject.rs +++ b/rstest_macros/src/render/inject.rs @@ -103,50 +103,8 @@ fn default_fixture_resolve(ident: &Ident) -> Cow { fn handling_magic_conversion_code(fixture: Cow, arg_type: &Type) -> Expr { parse_quote! { { - struct __Wrap(std::marker::PhantomData); - - trait __ViaParseDebug<'a, T> { - fn magic_conversion(&self, input: &'a str) -> T; - } - - impl<'a, T> __ViaParseDebug<'a, T> for &&__Wrap - where - T: std::str::FromStr, - T::Err: std::fmt::Debug, - { - fn magic_conversion(&self, input: &'a str) -> T { - T::from_str(input).unwrap() - } - } - - trait __ViaParse<'a, T> { - fn magic_conversion(&self, input: &'a str) -> T; - } - - impl<'a, T> __ViaParse<'a, T> for &__Wrap - where - T: std::str::FromStr, - { - fn magic_conversion(&self, input: &'a str) -> T { - match T::from_str(input) { - Ok(v) => v, - Err(_) => { - panic!("Cannot parse '{}' to get {}", input, std::stringify!(#arg_type)); - } - } - } - } - - trait __ViaIdent<'a, T> { - fn magic_conversion(&self, input: &'a str) -> T; - } - - impl<'a> __ViaIdent<'a, &'a str> for &&__Wrap<&'a str> { - fn magic_conversion(&self, input: &'a str) -> &'a str { - input - } - } - (&&&__Wrap::<#arg_type>(std::marker::PhantomData)).magic_conversion(#fixture) + use rstest::magic_conversion::*; + (&&&Magic::<#arg_type>(std::marker::PhantomData)).magic_conversion(#fixture) } } } diff --git a/src/render/mod.rs b/rstest_macros/src/render/mod.rs similarity index 100% rename from src/render/mod.rs rename to rstest_macros/src/render/mod.rs diff --git a/src/render/test.rs b/rstest_macros/src/render/test.rs similarity index 100% rename from src/render/test.rs rename to rstest_macros/src/render/test.rs diff --git a/src/render/wrapper.rs b/rstest_macros/src/render/wrapper.rs similarity index 100% rename from src/render/wrapper.rs rename to rstest_macros/src/render/wrapper.rs diff --git a/src/resolver.rs b/rstest_macros/src/resolver.rs similarity index 100% rename from src/resolver.rs rename to rstest_macros/src/resolver.rs diff --git a/src/test.rs b/rstest_macros/src/test.rs similarity index 99% rename from src/test.rs rename to rstest_macros/src/test.rs index 61f0537..f6ecae8 100644 --- a/src/test.rs +++ b/rstest_macros/src/test.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; use std::iter::FromIterator; -pub(crate) use mytest::{fixture, rstest}; +pub(crate) use rstest::{fixture, rstest}; pub(crate) use pretty_assertions::assert_eq; use proc_macro2::TokenTree; use quote::quote; diff --git a/src/utils.rs b/rstest_macros/src/utils.rs similarity index 99% rename from src/utils.rs rename to rstest_macros/src/utils.rs index 459d204..9471c7a 100644 --- a/src/utils.rs +++ b/rstest_macros/src/utils.rs @@ -276,7 +276,6 @@ mod test { use super::*; use crate::test::{assert_eq, *}; - use mytest::rstest; #[test] fn fn_args_idents_should() { diff --git a/rstest_reuse/src/lib.rs b/rstest_reuse/src/lib.rs index 9c418bb..4e2ecf0 100644 --- a/rstest_reuse/src/lib.rs +++ b/rstest_reuse/src/lib.rs @@ -124,7 +124,7 @@ //! test it_works::case_1::t_1 ... ok //! ``` //! -//! Template can also used for `#[values]` and `#[with] arguments if you need: +//! Template can also used for `#[values]` and `#[with]` arguments if you need: //! //! ``` //! use rstest::*; @@ -337,7 +337,7 @@ fn get_export(attributes: &[Attribute]) -> Option<&Attribute> { /// use a signature like if you're wrinting a standard `rstest`. /// /// If you need to export the template at the root of your crate or use it from another crate you -/// should annotate it with `#[export]` attribute. This attribute add #[macro_export] attribute to +/// should annotate it with `#[export]` attribute. This attribute add `#[macro_export]` attribute to /// the template macro and make possible to use it from another crate. /// /// When define a template you can also set the arguments attributes like `#[case]`, `#[values]`