Search code examples
pythontestingpytestpytest-dependency

Skipping specific parametrized pytests based on failure for specific parameters


I have some parametrized tests

def test1():
  #do test1
def test2():
  #do test2
def test3():
  #do test3

Each test is parametrized by

@pytest.mark.parametrize(x)

I would like to run these tests against

test_data=[1,2,3,4]

I have tried using pytest-depends

@pytest.mark.depends(on=['test1'])
@pytest.mark.parametrize(x)

However, I get that all test_2 are skipped if any test_1 fails. Instead, I would like test_2 for the specific parametrization to be skipped only if test_1 failed for the specific parametrization.

Can this be obtained in pytest?


Solution

  • This is possible, if you add separate dependencies for each parameter. In your example you could do:

    @pytest.mark.parametrize("x", [1, 2, 3 ,4])
    def test1(x):
        assert x != 2
    
    
    @pytest.mark.parametrize("x", [
        pytest.param(1, marks=pytest.mark.depends(on='test1[1]')),
        pytest.param(2, marks=pytest.mark.depends(on='test1[2]')),
        pytest.param(3, marks=pytest.mark.depends(on='test1[3]')),
        pytest.param(4, marks=pytest.mark.depends(on='test1[4]')),
    ])
    def test2(x):
        pass
    

    In this case, test2[2] will be skipped, because test1[2] fails.

    If you want to take the test data from a variable or function, or you don't want that much clutter in the parametrize decorator, you can also do this in a bit more generic way:

    test_data = [1, 2, 3, 4]
    
    def data_dependent_on(name):
        return [
            pytest.param(d, marks=pytest.mark.depends(on=f"{name}[" f"{d}]"))
            for d in test_data
        ]
    
    @pytest.mark.parametrize("x", test_data)
    def test1(x):
        assert x != 2
    
    @pytest.mark.parametrize("x", data_dependent_on("test2"))
    def test2(x):
        assert x != 3
    
    @pytest.mark.parametrize("x", data_dependent_on("test3"))
    def test3(x):
        pass
    

    In this case test2[2] will be skipped as before because test1[2] fails, test3[2] will be skipped because test2[2] fails, and test3[3] will be skipped because of the failing test2[3].

    This of course works only for one argument. If you have more than one argument, as mentioned in the comments, you have to adapt the code accordingly:

    @pytest.mark.parametrize("x, y", [(1, 2), (3, 4)])
    def test1(x, y):
        assert x != 1
    
    @pytest.mark.parametrize("x, y", [
        pytest.param(1, 2, marks=pytest.mark.depends(on='test1[1-2]')),
        pytest.param(3, 4, marks=pytest.mark.depends(on='test1[3-4]')),
    ])
    def test2(x, y):
        pass
    

    or in the more generic version:

    test_data = [(1, 2), (3, 4)]
    
    def data_dependent_on(name):
        return [
            pytest.param(d1, d2, marks=pytest.mark.depends(on=f"{name}[" f"{d1}-{d2}]"))
            for (d1, d2) in test_data
        ]
    
    @pytest.mark.parametrize("x, y", test_data)
    def test1(x, y):
        assert x != 1
    
    @pytest.mark.parametrize("x, y", data_dependent_on("test1"))
    def test2(x, y):
        pass