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

Pytest integration #124

Open
Tishka17 opened this issue Apr 8, 2024 · 15 comments
Open

Pytest integration #124

Tishka17 opened this issue Apr 8, 2024 · 15 comments
Assignees
Labels
enhancement New feature or request integrations
Milestone

Comments

@Tishka17
Copy link
Member

Tishka17 commented Apr 8, 2024

Allow using dishka container to provide fixtures

@Tishka17 Tishka17 added this to the 1.1 milestone Apr 8, 2024
@Tishka17 Tishka17 self-assigned this Apr 8, 2024
@Tishka17 Tishka17 added the enhancement New feature or request label Apr 8, 2024
@RonnyPfannschmidt
Copy link

im one of the pytest maintainers, im very curious about enabling this as i want to start using dishka more in a number of projects that have some needs and we will need pytest fixtures

we are plying with ideas on how to repressent pytest fixtures in a more interoperable manner and would love to get input on both - making pytest fixutres more easy to integrate as well as easing other di tools to cooperate with pytest nicely

a key pain point is probably that pytest currently works name based instead of type based

@Tishka17
Copy link
Member Author

Tishka17 commented Jul 29, 2024

I though about several approaches here:

  1. Generate fixures manually. So the code will be like
x = dishka_fixture("x", Class)

which will be transformed to

def x(dishka_container):
   return dishka_container.get(Class)
  1. Decorate tests and fixtures using @inject decorator as in all integrations. So it will erase all FromDishka arguments and add dishka_container
@inject
def test(a, x: FromDishka[Class]):
   ...

will be transofmed to something similar to

def test(a, dishka_container):
   _real_test(a, dishka_container.get(Class))
  1. Automatically wrap all tests and fixtures like in 2. I tried to achieve it here: dc8c8a0 but it works only with tests, not fixtures

Another task here is pytest_asyncio integration

@Tishka17
Copy link
Member Author

As a PoC

def dishka_fixture(cls: Any):
    def temp_fixture(dishka_container):
        return dishka_container.get(cls)

    return pytest.fixture(temp_fixture)

@pytest.fixture
def dishka_container():
    p = Provider(scope=Scope.APP)
    p.provide(lambda: 12, provides=int)
    return make_container(p)

someint = dishka_fixture(int)

def test_x(someint):
    assert someint == 12

@RonnyPfannschmidt
Copy link

It's critical to set the name, else thing's will turn bleak

@Tishka17
Copy link
Member Author

@RonnyPfannschmidt is it? I did only small test and that code looks working. I would prefer not to set name twice for same fixture

@RonnyPfannschmidt
Copy link

I may be missremember the exact factory parsing behavior

I'll validate later

@Tishka17
Copy link
Member Author

@RonnyPfannschmidt is there any hook to modify any fixture/test?

@RonnyPfannschmidt
Copy link

There's no hook to modify dependency injection yet

Same goes for discovery of fixtures

I'd like a discussion on enabling discovery of more fixtures and matching them to parameters

@Tishka17
Copy link
Member Author

Tishka17 commented Jul 30, 2024

@RonnyPfannschmidt
a) we can patch fixtures and tests so they will call container by themselves. It doesn't require modification of pytest DI engine. But it requires some kind middlewares/hooks which will be applied to any created test/fixture. I showed prototype for wrapping tests here dc8c8a0

b) probably pytest can be modified to lookup fixtures not only by name, but by types. In this case we need some fixture factory protocol in pytest implemented by pytest plugin (dishka). This looks much more complicated for me. We need to keep in mind here, that there will be multiple containers in different packages/modules

@RonnyPfannschmidt
Copy link

not to mention that pytest has own fixture scopes

instead of patching tests and fixtures, i'd like to have a way to be a intermediate between the container and the pytest system (so the normal fill fixture mechanisms can be used)

my current impression is that in the case of pytest, a certain focus on controlling the call signatures from the test framework is necessary

@Tishka17
Copy link
Member Author

Tishka17 commented Jul 30, 2024

Regarding scopes my point is that main logic of scopes is related to application running inside test, so here we do not need anything. If user needs some logic on synchronizing container scope (e.g, Scope.RUNTIME=="session" and Scope.APP=="funcion") it can be implemented in his dishka_container fixture

On topic of container managements I am interested in implementing integration which will be available on current versions of pytest, but it will be manual (using @inject or dishka_fixture as shown above). But for autoinjected I definitely need changes in pytest logic.

Do you have any issues/plans for this in pytest repo?

@RonnyPfannschmidt
Copy link

No plans or issues yet, the pre planning stage is literally starting now (in part because dishka is nicely inspiring)

@Tishka17 Tishka17 mentioned this issue Jul 30, 2024
1 task
@Tishka17
Copy link
Member Author

Another open question here is pytest-asyncio support. We have 2 versions of container now: sync and async. I guess, user might want to use async container to get fixtures for non-async test. And vice versa.

@RonnyPfannschmidt
Copy link

thats one of the reasons why the full-fillment of the injection needs to be able to be deferred to some kind of controller - in particualr since pytest is absolutlely unable to re-color test nodes in any sensible amount of time

@RonnyPfannschmidt
Copy link

pytest-dev/pytest#12473 is working on replacing fixtures with a more specific object

i believe that can be a starting point for more

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

No branches or pull requests

2 participants