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

Caching fixtures #182

Open
gunther9 opened this issue Mar 3, 2023 · 7 comments
Open

Caching fixtures #182

gunther9 opened this issue Mar 3, 2023 · 7 comments

Comments

@gunther9
Copy link

gunther9 commented Mar 3, 2023

Hi.

I want to implement a fixture that generates an initial state in my program, that will then use for various tests.

However, the code of that fixture can be slow to run (maybe I have to download some data), so I want to cache the output of that fixture so that it is only executed once even though it is used by multiple tests. How can I do this?

Of course, each test should have an independent copy of the output of that fixture, so they are completely independent.

@la10736
Copy link
Owner

la10736 commented Mar 3, 2023

You can use #[once] fixture to have a static reference to your expensive part https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture.

Maybe you need that your tests should modify this fixture too... you can follow the follow pattern

#[fixture]
#[once]
fn very_slow() -> A {
    // your expensive job
}

#[fixture]
fn dependency(very_slow: &A) -> A {
    very_slow.clone()
}

#[rstest]
fn test(dependency: A) {
    ....
}

In this case very_slow is execute just once for all your tests.

@gunther9
Copy link
Author

gunther9 commented Mar 4, 2023

I see. So #[once] has then a permanent memory, and if we were to pass a &mut reference to the test function and somehow changed the object, then those changes would persist to the other tests? I then also assume the tests are not run in parallel (which would create race conditions and weird behaviour)? Is this something that rstest allows in general, parallelized tests?

I would maybe then suggest creating a macro that encapsulates the behaviour of the first two fixtures, i.e. something the calculates a variable, but then yields copies of that variable, maybe something like a #[cached] macro.

@la10736
Copy link
Owner

la10736 commented Mar 6, 2023

In Rust you cannot have a shared mutable reference. If you need serial tests you can use serial_test crate but also in this case you cannot use shared mutable reference without unsafe code 😨

Anyway you can encapsulate you fixture behind a static reference of a Arc<Mux<F>> to work safely with a shared mutable fixture.... The down side here is that you test that should take a mutex guard will have a scared signature.

About your proposal maybe I can just introduce a clone option to once fixture that perform a .clone() on the static shared fixture. In this case the previous code become

#[fixture]
#[once(clone)]
fn very_slow() -> A {
    // your expensive job
}

#[rstest]
fn test(very_slow: A) {
    ....
}

@gunther9
Copy link
Author

gunther9 commented Mar 8, 2023

Hey,

Yeah, once(clone) could be good.

Yes, I understand the shared mutable reference issue. But if we don't have these and the tests are truly unit tests, is it possible to run them in parallel? For example pytest-xdist allows adding a -n flag setting the number of workers?

@gunther9
Copy link
Author

@la10736
What would be the best way to implement this once(clone) macro myself in the meantime? :)

@la10736
Copy link
Owner

la10736 commented Mar 22, 2023

Ok, should be quite simple to do.... but once use a some parts of code that I would remove and refactor to use a new approach introduced with the last refactor.

Let me some time to try to refactor it before and then I'll give all information of how to implement this feature.

@la10736
Copy link
Owner

la10736 commented Mar 22, 2023

Ok, I've refactored the code.
You should extend

is_global_await: bool,
once field that now contains just the parsed ident to carry out also these options (maybe an enum that replace Option<Ident> and contains also this variant is the more explicit way to do it).

To see an example of how implement both once and once(clone) parsing you can see in future module that parse both future and future(awt).

Please add both unit and integration tests, documentation on both README.md and rstest_macros/src/lib.rs and finaly a change log entry in CHANGELOG.md

Feel free to ask every question.

THX

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