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

Explicit control of fixtures outside of test run #3817

Open
jaraco opened this issue Aug 15, 2018 · 3 comments
Open

Explicit control of fixtures outside of test run #3817

jaraco opened this issue Aug 15, 2018 · 3 comments
Labels
topic: fixtures anything involving fixtures directly or indirectly type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature

Comments

@jaraco
Copy link
Contributor

jaraco commented Aug 15, 2018

Pytest fixtures have some really powerful features/properties:

  • inversion of control (plugins add new functionality)
  • dependency injection (fixtures declare their dependencies by fixture name)
  • multiple fixtures can depend on shared funcitonality.

When applying pytest fixtures to various services, this creates a powerful environment for orchestrating those services.

In one of our test suites, we have a more complicated form of the following fixtures:

@pytest.fixture(scope='session')
def api_service(mongodb_instance, postgres_instance, other_service):
    ...
    return ApiServiceDescription(...)

@pytest.fixture(scope='session')
def other_service(postgres_instance):
    ...
    return OtherServiceDescription(...)

These fixtures have some really nice properties that I would like to use outside running tests, in other phases of development, such as a development instance of the project under test. It would be nice to be able to start up api_service from the command line or Python script, let pytest deal with the dependencies, return control to the user (or sleep) for as long as needed, and clean up after.

Most importantly, I'd like to have one place where these fixtures (and their dependencies and startup/teardown logic) are defined, even if they're orchestrated in two environments (running tests and other manual invocations).

The most obvious way I can see to accomplish this would be for pytest to present an interface for 'running' fixtures. Any other approach I consider (using Docker compose, writing another orchestration framework) requires a lot of duplication of the functionality in the fixtures.

Is it possible to do this with the code as currently presented, maybe with just a few lines of code? I found the FixtureManager, which requires a Session which requires a Config object which requires a PluginManager. Can you suggest a way that someone might be able to hack together a fixture runner? Would you consider a feature to expose such functionality more simply, such that one could run something like:

$ pytest --run-fixture api_service

or maybe

$ python -m pytest.run-fixtures api_service
@RonnyPfannschmidt
Copy link
Member

currently its not externalized and thats a massive shortcoming as the internals are deeply integrated with way too much of test executing

@pawciobiel
Copy link

@jaraco
I don't know if you thought about it and I don't know if it would meet all your requirements but I think something like the following could work too:

pytest --run-fixture-service-via-ansible=what-knot.yml

or

pytest --run-fx-service-via-docker=panoptic-pure-img
pytest --run-fx-service-via-docker=panoptic-bundle-img

@jaraco
Copy link
Contributor Author

jaraco commented Sep 18, 2018

I've had limited success with a script like this for running a particular fixture:

"""
A hacky-as-hell technique to run a Panoptic instance
with all its dependencies using the PyTest fixtures
technique.

Preferably, pytest could satisfy this use case as
described in
https://github.com/pytest-dev/pytest/issues/3817
"""

import sys
import subprocess
from textwrap import dedent

import path


template = dedent('''
    def null():
        """
        A hook to trigger the panoptic_instance fixture.

        >>> x = __import__('os').environ.pop('PYTHONPATH', None)
        >>> panoptic = getfixture('panoptic_instance')
        Starting...
        >>> import time; time.sleep(86400)
        """
    ''')


def main():
    with path.TempDir() as root:
        mod = root / 'module.py'
        mod.write_text(template)
        cmd = [
            sys.executable,
            '-m', 'pytest',
            '--doctest-modules',
            '--capture=no',
            mod,
        ] + sys.argv[1:]
        subprocess.check_call(cmd)


__name__ == '__main__' and main()

This single-file will launch the indicated fixture and then sleep forever.

I don't love it, but it seems to be adequate enough for the job, and has some nice properties:

  • full setup and teardown of the fixture (and its dependent fixtures)
  • responds to Ctrl+C to stop everything cleanly

@Zac-HD Zac-HD added type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature topic: fixtures anything involving fixtures directly or indirectly labels Oct 20, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: fixtures anything involving fixtures directly or indirectly type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature
Projects
None yet
Development

No branches or pull requests

4 participants