Skip to content

Can plugins define custom ways to load fixtures? #4636

Closed

Description

@merwok

Not sure if this is a feature request or a documentation issue!

I would like to define fixtures in YAML files. I could write a yaml-loader fixture + use parametrize to give a filename to the loader, but it would be even better if I could instead write test_something(some_data) + some_data.yml and have the data from the YAML file loaded and passed to the test function.

(I am not asking about a YAML test collector, as found in an example in the docs and in plugins like yamlwsgi.)

From reading docs and tickets, it is not clear to me if this is possible.
It looks like pytest_runtest_setup might be able to transform a test function before it’s run, but the documentation on that hook is very scarce.

Activity

  1. added
    type: questiongeneral question, might be closed after 2 weeks of inactivity
    on Jan 12, 2019
  2. nicoddemus commented on Jan 12, 2019

    @nicoddemus
    Member

    Hi @merwok

    if I could instead write test_something(some_data) + some_data.yml and have the data from the YAML file loaded and passed to the test function.

    Sorry, I didn't quite understand that, can you please elaborate?

  3. asottile commented on Jan 12, 2019

    @asottile
    Member

    I think I understand the question, but don't know enough about the pytest internals to answer.

    Basically the ask is to have something like:

    testing/
        data/
            foo.yaml
            bar.yaml
    

    And then something that allows you to generate fixtures so tests like this would work:

    def test_foo(foo):
        ...  # the `foo` fixture contains the loaded contents of testing/data/foo.yaml
    
    def test_bar(bar):
        ...  # the `bar` fixture contains the loaded contents of testing/data/bar.yaml
  4. merwok commented on Jan 13, 2019

    @merwok
    ContributorAuthor

    Yes this is the idea!

  5. asottile commented on Jan 13, 2019

    @asottile
    Member

    I found this: #2424 (comment) not sure if it implements exactly what you're looking for but looks promising.

    A little bit of hackery, but it looks like it works:

    # $ tail -n999 testing/* tests/* | sed 's/^=/### =/g'
    ### ==> testing/file_1.yaml <==
    f1: 1
    
    ### ==> testing/file_2.yaml <==
    f2: 2
    
    ### ==> testing/file_3.yaml <==
    f3: 3
    
    ### ==> testing/file_4.yaml <==
    f4: 4
    
    ### ==> testing/file_5.yaml <==
    f5: 5
    
    ### ==> tests/conftest.py <==
    import os.path
    
    import pytest
    import yaml
    
    DATA = os.path.join(os.path.realpath(os.path.dirname(__file__)), '../testing')
    
    
    def _fixture(filename):
        def fixture_func():
            with open(os.path.join(DATA, filename)) as f:
                return yaml.safe_load(f)
    
        fixture_func.__name__, _ = os.path.splitext(filename)
        fixture_func.__doc__ = f'Loads {filename} from ./testing'
    
        # call fixture late so `__doc__` shows up in `pytest --fixtures`
        return pytest.fixture(fixture_func)
    
    
    globals().update({
        os.path.splitext(f)[0]: _fixture(f) for f in os.listdir(DATA)
    })
    
    ### ==> tests/test1.py <==
    def test(file_1, file_2):
        assert file_1 == file_2
    $ pytest tests/test1.py 
    ============================= test session starts ==============================
    platform linux -- Python 3.6.7, pytest-4.1.1, py-1.7.0, pluggy-0.8.1
    rootdir: /tmp/t, inifile:
    collected 1 item                                                               
    
    tests/test1.py F                                                         [100%]
    
    =================================== FAILURES ===================================
    _____________________________________ test _____________________________________
    
    file_1 = {'f1': 1}, file_2 = {'f2': 2}
    
        def test(file_1, file_2):
    >       assert file_1 == file_2
    E       AssertionError: assert {'f1': 1} == {'f2': 2}
    E         Left contains more items:
    E         {'f1': 1}
    E         Right contains more items:
    E         {'f2': 2}
    E         Use -v to get the full diff
    
    tests/test1.py:2: AssertionError
    =========================== 1 failed in 0.06 seconds ===========================

    even threw in a little bit to make pytest --fixtures output work correctly:

    $ pytest --fixtures | tail -14
    
    ------------------------ fixtures defined from conftest ------------------------
    file_1
        Loads file_1.yaml from ./testing
    file_2
        Loads file_2.yaml from ./testing
    file_3
        Loads file_3.yaml from ./testing
    file_4
        Loads file_4.yaml from ./testing
    file_5
        Loads file_5.yaml from ./testing
    
    ========================= no tests ran in 0.01 seconds =========================
  6. merwok commented on Jan 13, 2019

    @merwok
    ContributorAuthor

    That’s interesting! Using module-level __dir__ and __getattr__ I could even avoid mucking with globals. I will try this idea, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: questiongeneral question, might be closed after 2 weeks of inactivity

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions