pytest-dependency icon indicating copy to clipboard operation
pytest-dependency copied to clipboard

Conditional dependency on 2 tests

Open rnetser opened this issue 3 years ago • 2 comments

Is there a way to have a test that is conditional dependent on 2 tests? We have 2 tests (test_1 and test_2) that are mutually exclusive; we control which test to run by using a marker. We have a test which should depend on either one of them. Is there a way to achieve it?

Pseudo code of what is needed:

@pytest.mark.dependency()
def test_1():
    return

@pytest.mark.dependency()
def test_2():
    return

@pytest.mark.dependency(depends="test_1" or "test_2")
def test_3():
    return

rnetser avatar Dec 24 '21 09:12 rnetser

This is not supported out of the box. But you can implement it using a small helper function:

import pytest
from pytest_dependency import depends

def depends_or(request, other, scope='module'):
    """Add dependency on any of the other tests.

    Call pytest.skip() unless a successful outcome of any of the tests
    in `other` has been registered previously.  This helper is similar
    to `pytest_dependency.depends()`.  It takes the same arguments.
    But while `pytest_dependency.depends()` combines the tests in
    `other` in an and-like manner, it skips the current test unless
    all other tests did succeed, this function combines them in an
    or-like manner, it runs the current test if at least one of the
    other tests did succeed.
    """
    item = request.node
    for o in other:
        try:
            depends(request, [o], scope)
        except pytest.skip.Exception:
            continue
        else:
            return
    pytest.skip("%s depends on any of %s" % (item.name, ", ".join(other)))


@pytest.mark.dependency()
def test_ap():
    pass

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_ax():
    assert False

@pytest.mark.dependency()
def test_bp():
    pass

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_bx():
    assert False

@pytest.mark.dependency()
def test_c(request):
    depends_or(request, ["test_ax", "test_bx"])    
    pass

@pytest.mark.dependency()
def test_d(request):
    depends_or(request, ["test_ax", "test_bp"])    
    pass

@pytest.mark.dependency()
def test_e(request):
    depends_or(request, ["test_ap", "test_bx"])    
    pass

@pytest.mark.dependency()
def test_f(request):
    depends_or(request, ["test_ap", "test_bp"])    
    pass

Tests test_c, test_d, test_e, and test_f all depend on either test_a or test_b in the manner you described. I included succeeding and failing incarnations of test_a and test_b to demonstrate the effect. Only test_c will be skipped, because both tests in its dependency list fail, the other ones are run, because they have at least on succeeding test in their dependency list.

Warning: as usual, the documentation of internal data structures in pytest is scarce. It is not documented at all that skipping of tests internally work by raising a Skipped exception and this exception class is exposed in the pytest.skip.Exception attribute of pytest.skip(). As usual I needed to find out by reading the pytest source code. There is thus no guarantee that this is stable across pytest versions and I did not check whether it works with all versions supported by pytest-dependency.

RKrahl avatar Dec 24 '21 16:12 RKrahl

Great, will try it out. Thank you

rnetser avatar Dec 27 '21 09:12 rnetser