[pytest-dev] solving the "too static" fixture scope problem

holger krekel holger at merlinux.eu
Fri Oct 11 12:57:24 CEST 2013


On Fri, Oct 11, 2013 at 12:51 +0200, Florian Schulze wrote:
> I would expect the behaviour you describe for scope="each" to be the default for a fixture without a scope. Why introduce a new scope for that? Am I missing something? I haven't used pytest for such complex things yet.

Defaulting to scope="each" is an interesting proposition but it
would be largely backward-compatible and i think in real situations. 
Consider a fixture like this:

    @pytest.fixture
    def db(...):
        return DbInstance()

and then a test and other fixtures using this "db" fixture.  They
would all get distinct instances (people using fixtures more extensively
implicitly rely on the current per-test "sharing" of resources and i think
it makes sense, just not for all resources).

> Regarding the backward compatibility issue you described, I would expect the new behaviour instead of the current one. But maybe I'm missing something, because of the abstract example.

If you refer to tmpdir/monkeypatch and potentially others, i agree
that the current behaviour is more surprising (sharing the tmpdir
across multiple fixtures which don't even neccessarily know about each
other).

cheers,
holger

> Regards,
> Florian Schulze
> 
> On 11.10.2013, at 10:40, holger krekel <holger at merlinux.eu> wrote:
> 
> > Hi pytest users and developers,
> > 
> > I'd like to discuss and get feedback on 
> > extending the fixture mechanism and fix what
> > i consider a biggest limitation at the moment.
> > 
> > Problem: fixtures are tied statically to a scope
> > =================================================
> > 
> > For example, you cannot use monkeypatch in a higher
> > than "function" scoped fixture. Same is true for
> > tmpdir and probably also many user-defined fixtures.
> > I've certainly had this problem myself many times
> > that i had a fixture function that didn't really
> > care in what scope it was used.  There are
> > ways to get around this problem but they are not
> > pretty:
> > 
> >    @pytest.fixture(scope="module")
> >    def myfix_module(request
> >        return _myfix(request)
> > 
> >    @pytest.fixture(scope="function")
> >    def myfix_function(request
> >        return _myfix(request)
> > 
> > where _myfix is the function that doesn't
> > care about the actual scope.  Even then, you
> > can't use builtin fixtures like "monkeypatch",
> > "tmpdir", etc.
> > 
> > Solution Idea: introduce "each" scoped fixtures
> > =====================================================
> > 
> > The idea is allow a fixture function to declare it wants
> > to be used in the scope of the requesting fixture function
> > (or at function-scope if used from a test).
> > 
> > This is how "monkeypatch" would be implemented then:
> > 
> >    @pytest.fixture(scope="each")
> >    def monkeypatch(request):
> >        ...  # same implementation as in _pytest/monkeypatch.py
> > 
> > The new "each" scope means that each fixture/test requesting
> > the "monkeypatch" fixture would receive its own fixture instance.
> > 
> > So a session-scoped fixture could naturally use it like this:
> > 
> >    @pytest.fixture(scope="session")
> >    def myfix(monkeypatch):
> >        monkeypatch.setattr(...)
> >        return some_value
> > 
> > The passed in monkeypatch object here is a specific instance just 
> > for the ``myfix`` fixture function: "each" fixture function
> > requesting ``monkeypatch`` gets a new instance of it.  
> > If e.g. a test uses another module-scoped fixture defined like this:
> > 
> >    @pytest.fixture(scope="module")
> >    def myfix2(monkeypatch):
> >        mp.setattr(...)
> >        return some_value
> > 
> > this would invoke the ``monkeypatch`` fixture function a second time, 
> > resulting in a new instance for use by the ``myfix2`` instance.
> > 
> > The same logic could be applied to other fixtures
> > like "tmpdir" or user-defined ones.
> > 
> > Do you like this idea? Would you find it helpful for your test suites?
> > 
> > There is one issue i am not sure about yet, however.  Currently,
> > when a test requires fixture A and B, and B requires C and C requires A,
> > then the two "A" would be exactly the same object, independently of what
> > which scopes are declared.  If A=="tmpdir", then the test's tmpdir and 
> > C's tmpdir would be the same directory.  I often don't find this desirable.  
> > If tmpdir would be an "each" scoped fixture, then C and the test would 
> > each receive a clean new tmpdir.  If that is a backward-compat issue, 
> > we could introduce another name for the new "each" scoped tmpdir.
> > I usually find myself working around the problem of a "tmpdir"
> > shared by multiple different fixtures, though.
> > 
> > cheers,
> > holger
> > _______________________________________________
> > Pytest-dev mailing list
> > Pytest-dev at python.org
> > https://mail.python.org/mailman/listinfo/pytest-dev
> 


More information about the Pytest-dev mailing list