Skip to content

Can plugins define custom ways to load fixtures? #4636

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

Closed
merwok opened this issue Jan 11, 2019 · 5 comments
Closed

Can plugins define custom ways to load fixtures? #4636

merwok opened this issue Jan 11, 2019 · 5 comments
Labels
type: question general question, might be closed after 2 weeks of inactivity

Comments

@merwok
Copy link
Contributor

merwok commented Jan 11, 2019

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.

@Zac-HD Zac-HD added the type: question general question, might be closed after 2 weeks of inactivity label Jan 12, 2019
@nicoddemus
Copy link
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?

@asottile
Copy link
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

@merwok
Copy link
Contributor Author

merwok commented Jan 13, 2019

Yes this is the idea!

@asottile
Copy link
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 =========================

@merwok
Copy link
Contributor Author

merwok commented Jan 13, 2019

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

@Zac-HD Zac-HD closed this as completed Jan 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question general question, might be closed after 2 weeks of inactivity
Projects
None yet
Development

No branches or pull requests

4 participants