jeudi 23 avril 2020

Pytest - TestClass for a Fixture with class scope

I have many datasets stored each in a different file. My aim is to run a couple of tests for each dataset while the datasets should be loaded only once for each set of tests.

My idea is to group the tests into a TestClass and load the datasets by using a fixture that should havescope="class":

import pytest

data = [[0,0],[1,1],[2,2]]

@pytest.fixture(scope="class", params=data)
def get_data(request):
    my_dataset = request.param
    yield my_dataset

class TestData:
    def test_first(self, get_data):
        assert(len(get_data) == 2)

    def test_second(self, get_data):
        assert(1)

This seems to work as expected but when trying to apply this idea to the actual problem I run into an error after the first 8 tests (of 376):

import pytest
import pickle
from itertools import product

list1 = [...]
list2 = [...]
list3 = [...]
param_set = list(product(list1, list2, list3))


@pytest.fixture(scope="class", params=param_set)
def get_data(request):
    p1, p2, p3 = request.param

    data_path = "some_pathname_depending_on_{}_{}_{}.pkl".format(p1, p2, p3)

    with open(data_path, "rb") as handle:
        yield pickle.load(handle)


class TestClass:
    def test_first(self, get_data)
        assert(1)


    def test_second(self, get_data):
        assert(1)

The error I obtain for all later tests is

test setup failed
cls = <class '_pytest.runner.CallInfo'>
func = <function call_runtest_hook.<locals>.<lambda> at 0x7f5b7f44ec80>
when = 'setup'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(cls, func, when, reraise=None):
        #: context of invocation: one of "setup", "call",
        #: "teardown", "memocollect"
        start = time()
        excinfo = None
        try:
>           result = func()

../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/runner.py:229: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/runner.py:201: in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
../../SYK_proj/venv2/lib/python3.5/site-packages/pluggy/hooks.py:286: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
../../SYK_proj/venv2/lib/python3.5/site-packages/pluggy/manager.py:92: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
../../SYK_proj/venv2/lib/python3.5/site-packages/pluggy/manager.py:86: in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/runner.py:118: in pytest_runtest_setup
    item.session._setupstate.prepare(item)
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/runner.py:366: in prepare
    col.setup()
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/python.py:1427: in setup
    fixtures.fillfixtures(self)
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/fixtures.py:291: in fillfixtures
    request._fillfixtures()
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/fixtures.py:464: in _fillfixtures
    item.funcargs[argname] = self.getfixturevalue(argname)
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/fixtures.py:474: in getfixturevalue
    return self._get_active_fixturedef(argname).cached_result[0]
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/fixtures.py:490: in _get_active_fixturedef
    self._compute_fixture_value(fixturedef)
../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/fixtures.py:571: in _compute_fixture_value
    fixturedef.execute(request=subrequest)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureDef argname='get_data' scope='class' baseid='test_gs_data.py'>
request = <SubRequest 'get_data' for <Function test_first[get_data4]>>

    def execute(self, request):
        # get required arguments and register our own finish()
        # with their finalization
        for argname in self.argnames:
            fixturedef = request._get_active_fixturedef(argname)
            if argname != "request":
                fixturedef.addfinalizer(functools.partial(self.finish, request=request))

        my_cache_key = self.cache_key(request)
        cached_result = getattr(self, "cached_result", None)
        if cached_result is not None:
            result, cache_key, err = cached_result
>           if my_cache_key == cache_key:
E           ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

../../SYK_proj/venv2/lib/python3.5/site-packages/_pytest/fixtures.py:888: ValueError

Assertion failed

Assertion failed

If I change the fixture scope back to scope="function" all tests are passed but the dataset is loaded for every single test.

I am using python 3.5 and pytest 5.2.2 in pycharm.

Aucun commentaire:

Enregistrer un commentaire