rstest icon indicating copy to clipboard operation
rstest copied to clipboard

Possibility of resolving a fixture only once per test?

Open stevepryde opened this issue 1 year ago • 1 comments

This might be my misunderstanding of how fixtures are supposed to work, but I would love the ability to have fixtures resolved exactly once per test.

For example, I have a mock struct (let's call it MyMock) that contains the current mocked state. In my tests I want to use this struct to add some things to the current test environment.

For example:

#[test]
fn test_thing() {
    let mock = MyMock::new();
    let foo1: Foo = mock.add_foo("foo1");
    let foo2: Foo = mock.add_foo("foo2");

    // Now run the function-to-be-tested, which also requires `&MyMock`
    let result = my_func(&mock).unwrap();
    assert_eq!(result, foo1); // I need foo1 here.
}

Can I use fixtures to get both MyMock as well as one or more Foos, created from the same instance of MyMock ?

For example, something like this?

#[fixture]
fn mock() -> MyMock {
    MyMock::new()
}

#[fixture]
fn foo(mock: &MyMock, #[default = "foo1"] name: &str) -> Foo {
    mock.add_foo(name)
}

#[rstest]
fn test_thing(mock: &MyMock, #[with("foo1")] #[from(foo)] foo1: Foo, #[with("foo2")] #[from(foo)] foo2: Foo) {
    let result = my_func(&mock).unwrap();
    assert_eq!(result, foo1);
}

Requirements:

  • I want a new instance of MyMock per test.
  • I want that instance to be shared by any fixtures used in the same test (and their fixture dependencies, and so on)

Is this possible?

--

(as an aside, it gets tedious to write this kind of thing:

#[with("foo1")] #[from(foo)] foo1: Foo, #[with("foo2")] #[from(foo)] foo2: Foo

is there any way to improve that?)

stevepryde avatar Jun 21 '24 02:06 stevepryde

Yeah I think this is a great idea: my reading of the docs says that the #[once] fixture will be once at all (over all tests), rather than once per test. That seems like something that'd be nice to have.

There is discussion in #180 on fixtures too, and similarity to pytest function scope:

Sorry.... I've missed your point. You're right that's the expected behavior (the default pytest behavior is the function scope). Due to Rust's ownership model you cannot use the same instance in two places but just share reference...

Of course it is possible to implement something like this (I've already thought about it): a scoped once instance where you compute the object only if you don't already have one for the running asked scope, but that could not be the default behavior because in this case you cannot return the ownership of the fixture or a mutable reference to it but just a &'static of the fixture.

That idea seems like it's still awaiting someone to work on it

lmmx avatar Apr 24 '25 21:04 lmmx