How do you parametrize a pytest class with Hypothesis @given?

1.7k Views Asked by At

In order to test how my database behaves when I add two very similar data rows, I need to setup a new database for each combination of parameters. I'm also using Hypothesis' strategies to generate "similar" data rows.

The test work flow should go as:

for example in hypothesis-given-examples:    # @given
    for combination in pytest-parametrized-combinations:   # @pytest.mark.parametrize
        db = setup_db(example, combination)  # should be a fixture with `yield` but I can't parametrize it
        do_test_1(db)  # using the setup database
        do_test_2(db)  # using the setup database
        do_test_3(db)  # using the setup database
        teardown(db)

I started with:

@mark.parametrize('different_properties', [False, 'entirekeys', 'crop', 'addkeys', 'overwritekeys'])
@mark.parametrize('different_identproperties', [False, 'entirekeys', 'crop', 'addkeys', 'overwritekeys'])
@mark.parametrize('different_labels', [False, 'entirekeys', 'crop', 'addkeys', 'overwritekeys'])
class TestMerge:
    @classmethod
    @given(node1=node_strategy, sampler=data())
    def setup_class(cls, node1, sampler, different_labels, different_properties, different_identproperties):
        node2 = create_node_from_node(node1, sampler, different_labels, different_properties,
                                      different_identproperties)  # uses hypothesis to generate
        cls.node1, cls.node2 = node1, node2

    def test_merge(self):
        read_node1, read_node2 = read(1), read(2)
        # do some test here comparing input and output

But it seems that is impossible to do a setup like this

So I tried to use a fixture with an indirect argument, but I want to do this for a whole class and copying the parameters for each test method seems ridiculous and also I can't make a cartesian product of parameter lists using indirect arguments that feed to one fixture.

Here is a minimal running/erroring example:

import pytest
from hypothesis import strategies as st
from hypothesis import given

@pytest.mark.parametrize('different_properties', [False, 'entirekeys', 'crop', 'addkeys', 'overwritekeys'], scope='class')
@pytest.mark.parametrize('different_identproperties', [False, 'entirekeys', 'crop', 'addkeys', 'overwritekeys'], scope='class')
@pytest.mark.parametrize('different_labels', [False, 'entirekeys', 'crop', 'addkeys', 'overwritekeys'], scope='class')
class TestMerge:
    @classmethod
    @given(node1=st.integers(), sampler=st.data())
    def setup_class(cls, node1, sampler, different_labels, different_properties, different_identproperties):
        node2 = node1 * 2  # just for the minimal example
        cls.node1, cls.node2 = node1, node2

    def test_merge(self, different_labels, different_properties, different_identproperties):
        # read_node1, read_node2 = read(1), read(2)
        # do some test here comparing input and output
        pass

Pytest output:

ERROR test_minimal_example.py::TestMerge::test_merge[False-False-False] - TypeError: setup_class() missing 3 required...
ERROR test_minimal_example.py::TestMerge::test_merge[False-False-entirekeys] - TypeError: setup_class() missing 3 req...
    ...

Hopefully you can see what I'm trying to do. If not, please ask!

Thanks!

1

There are 1 best solutions below

3
On

It's not clear why you're using a class here. Try e.g.

@pytest.mark.parametrize("p1", [True, False])
@pytest.mark.parametrize("p2", [True, False])
@given(ex=st.integers())
def test(p1, p2, ex):
    with setup_db(ex, (p1, p2)) as db:
        do_test_1(db)
        do_test_2(db)
        do_test_3(db)

(and note that this also works for test methods, if you add a self in the appropriate spot)