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

Allow defining fixture session scope #119

Closed
svenstaro opened this issue May 2, 2021 · 7 comments
Closed

Allow defining fixture session scope #119

svenstaro opened this issue May 2, 2021 · 7 comments

Comments

@svenstaro
Copy link
Contributor

Hey, you continue to output amazing work!

However, I wonder whether it'd be possible to allow fixtures to run within certain scopes such as per session, per module, per test (the default).

You can currently kind of emulate the once per session scope with a global atomic but it'd be great if something like this were built into rstest with an attribute.

Reference from pytest feature: https://docs.pytest.org/en/6.2.x/fixture.html#scope-sharing-fixtures-across-classes-modules-packages-or-session

@la10736
Copy link
Owner

la10736 commented May 15, 2021

I though a lot in the last years about this feature and I never found any good solution. The real issue here is not to define the scope but find when the scope terminate and we should release the resources ... in practice there is no way hook your teardown.

Ok, not all cases need it and lot of times we can don't care about release our fixtures but that's not true in general: for instance your fixture is just a docker instance that you would like to stop when all your tests that need it are done.

So when I don't need to drop my static fixture I just use a lazy static reference to it but if I need to destroy I switch to Arc where I know that sometimes I'll build and destroy my fixture more that once but if I add some hysteresis I can reduce this problem.

Anyway, maybe it's time to call a stop loss and start to accept this limitation. I can code a basic implementation where you can mark as static your fixture and you know that the drop will never invoked. Maybe later I can think about to implement the the Arc version to give some little flexibility.

Ok... as I just say before you're my best customer and I can't ignore your requests.

@svenstaro
Copy link
Contributor Author

Any chance I can bug you into taking a look at this one of these days? :)

@la10736
Copy link
Owner

la10736 commented Sep 5, 2021

Ok, I'll try to find a slot in the next weeks.

@la10736
Copy link
Owner

la10736 commented Oct 9, 2021

I decided to implement a once attribute for fixture. once means that your fixture have a static lifetime and will be called just once for all your tests.

In this case the fixture will return a static reference to the static instance of your fixture and the fixture will never drop. That's the real limitation of this implementation: you cannot use it for something that you need to release at the end of your tests.

We can fine some crates that implement this behavior like lazy_static and once_cell but we cannot force the users to include these crates. So I decided to implement it by just a little unsafe (like lazy_static do under the hood).

use std::sync::Once;

struct MyStruct(u32);

fn get() -> &'static MyStruct {
    static mut S: Option<MyStruct> = None;
    static CELL: Once = Once::new();
    CELL.call_once(|| unsafe { S = Some(MyStruct(rand::random())) });
    unsafe { S.as_ref().unwrap() }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn all_threads_should_have_same_state() {
        let threads = (0..10000)
            .into_iter()
            .map(|_| std::thread::spawn(|| get().0))
            .collect::<Vec<_>>();

        let values = threads
            .into_iter()
            .map(|t| t.join().unwrap())
            .collect::<std::collections::HashSet<_>>();

        assert_eq!(1, dbg!(values).len());
    }
}

Syntax

Use #[once] to mark your fixture and use a reference where you call the fixture:

#[fixture]
#[once]
fn my_fixture() -> F {
 ....
}

#[rstest]
fn my_test(my_fixture: &F) {
  ....
}

la10736 added a commit that referenced this issue Oct 24, 2021
@la10736 la10736 changed the title Allow defining fixture scopes ala pytest Allow defining fixture session scope Oct 24, 2021
@la10736
Copy link
Owner

la10736 commented Oct 24, 2021

Ok base test and implementations are in place.

What is missed:

  • More tests
    • fixture without return
    • async once fixture should raise Error
    • fixture with generics should raise Error
    • fixture with impl in I/O arguments should raise Error
    • Test unit static impl
  • Documentation

@la10736
Copy link
Owner

la10736 commented Nov 27, 2021

Is not possible use #[once] with generics because should have a concrete and defined input and output. The real constrain is on the output type but also use generics on input type is unsound.

Also impl in input/output doesn't have too much sense.

la10736 added a commit that referenced this issue Nov 27, 2021
la10736 added a commit that referenced this issue Nov 27, 2021
@la10736
Copy link
Owner

la10736 commented Nov 27, 2021

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants