Skip to content
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

Add option to force the dependency to be collected #37

Open
pecalleja opened this issue Jan 24, 2020 · 5 comments
Open

Add option to force the dependency to be collected #37

pecalleja opened this issue Jan 24, 2020 · 5 comments
Labels
enhancement New feature or request

Comments

@pecalleja
Copy link

It would be nice an option in the dependency that force to collect and execute the required test. For example:

import pytest

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

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

@pytest.mark.dependency(depends=["test_a"])
def test_c():
    pass

@pytest.mark.dependency(depends=["test_b"], collect=True)
def test_d():
    pass

@pytest.mark.dependency(depends=["test_b", "test_c"])
def test_e():
    pass

if execute only the test_d the collector also will select the test_b because the hard restriction of the dependency collect=True

If you think this is a good idea I can work on this feature. kind regards

@RKrahl
Copy link
Owner

RKrahl commented Jan 25, 2020

I'm skeptical. Until now, pytest-dependency is neat and small because it has a very clear scope and that is about skipping tests. In principle, I'd prefer to keep the scope focused and keep out of neighbouring tasks such as collecting or ordering.

That does not mean that I'm totally against such an idea. But it does mean that it might take a significant effort to convince me that going that way is possible without opening a can of worms.

@RKrahl RKrahl added the enhancement New feature or request label Jan 25, 2020
@pecalleja
Copy link
Author

pecalleja commented Jan 27, 2020

Yeah, I agree with you about this, let's keep this awesome project neat and small. Just as a proof of concept I added in my conftest.py file this function:

def pytest_collection_modifyitems(config, items):
    dependencies = list()
    items_dict = dict()
    for item in items:
        markers = item.own_markers
        items_dict[item.name] = item
        for marker in markers:
            depends = marker.kwargs.get("depends")
            parent = item.parent
            collect = marker.kwargs.get("collect")
            if marker.name == "dependency" and depends and collect:
                for depend in depends:
                    dependencies.append((depend, parent))

    for dependency, parent in dependencies:
        if dependency not in list(items_dict.keys()):
            item = pytest.Function(name=dependency, parent=parent)
            items.insert(0, item)
            items_dict[dependency] = item

this isn't a optimal solution but it work for me .

@pecalleja
Copy link
Author

Taking the first comment example, imagine the case when you need to set test_d dependency with test_b. One option would be to call one test into the other :

import pytest

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

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

@pytest.mark.dependency(depends=["test_a"])
def test_c():
    pass

@pytest.mark.dependency()
def test_d():
    test_b()
    pass

@pytest.mark.dependency(depends=["test_b", "test_c"])
def test_e():
    pass

this solution force to execute the test_b but this solution let's test_b get out of pytest report of collected tests. also when calling the entire file, the test_b will be called twice... this sorts of thing I want to resolve with an option forcing to collect the "required" test dependency.

@a-recknagel
Copy link

@RKrahl Just to add my voice, I'd also appreciate such a feature, maybe enabled with a flag that I can put in my .ini though, adding a keyword to all functions creates a lot of noise. My use case is that Pycharm gives all my tests little play-buttons that I can press to run them, which just calls the pytest path/to/test.py::test_function form, and is very handy. Without pulling dependencies into the collection, I'd have to restructure my tests, think, or just run the whole suite, which all incur a non-trivial cost.

@pecalleja I tried your snippet, it didn't work for me, even after changing item = pytest.Function(name=dependency, parent=parent) to item = pytest.Function.from_parent(name=dependency, parent=parent) after pytest complained about the former.

@marcinwrochna
Copy link

marcinwrochna commented Jun 13, 2023

I was very surprised than selecting a test doesn't also select its dependencies and that there is no option or at least documentation on how to change that.

I had to modify pecalleja's snippet as follows:

@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
    seen = set[str]()
    new_items = list[pytest.Item]()

    def dfs(item: pytest.Item) -> None:
        if item.name in seen:
            return
        seen.add(item.name)
        for marker in item.own_markers:
            if marker.name == "dependency" and marker.kwargs.get("collect"):
                for dependency in marker.kwargs.get("depends", []):
                    dfs(pytest.Function.from_parent(name=dependency, parent=item.parent))
        new_items.append(item)

    for item in items:
        dfs(item)

    old_names = set(item.name for item in items)
    new_names = [item.name for item in new_items if item.name not in old_names]
    if new_names:
        print("Un-deselected:", *new_names)

    items[:] = new_items

This assumes just simple function dependencies in the same parent module; I don't know how to do it in general, but #56 has more.
It would be helpful if pytest-dependency at least documented how to create the dependency pytest.Item from a dependency marker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants