hitchdev / hitchstory

Type-safe YAML integration tests. Tests that write your docs. Tests that rewrite themselves.
https://hitchdev.com/hitchstory
Other
89 stars 3 forks source link

Accessing Pytest fixtures from Engine #7

Open timmartin opened 1 year ago

timmartin commented 1 year ago

I'm testing some Django code, and I'd like to use the Django test client. This is available in Pytest via pytest-django providing a client fixture.

It's meant to be used like this:

def test_with_client(client):
    response = client.get('/')
    assert response.content == 'Foobar'

I think the most natural way to adapt this to the Hitchstory approach is to add the decorator to my test_foo Pytest function:

def test_foobar(client):
    hs.named("Something with a client").play()

but it's not obvious there's a clean way to pass the client object through so that the engine can get at it. I think the client is recreated with each test (it potentially holds state) so we don't want to create the client once and include it when instantiating the engine.

crdoconnor commented 1 year ago

Hi Tim,

Thank you for trying it out!

The cleanest way right now would probably be to instantiate the engine class in the test method.

The down-side of this approach of course is that you would have to create a new method for each test and you wouldn't be able to use this method to seamlessly convert user stories into tests:

collection.with_external_test_runner().ordered_by_name().add_pytests_to(
       module=__import__(__name__) # This module
)

So, it would work fine if you have 3-5 tests but would get annoying if you had more.

I feel like the cleanest approach would, in your case, be to add a fixtures parameter to the "add_pytests_to" method. Would you like me to add this feature so you can do that?

E.g.

collection.with_external_test_runner().ordered_by_name().add_pytests_to(
       module=__import__(__name__),
       fixtures=[client],
)

And in the step methods you could reference "self.fixtures.client".

If you're keen on this idea, I could potentially add this feature a bit later on today and push out a new release.

On Wed, Jun 7, 2023 at 1:45 PM Tim Martin @.***> wrote:

I'm testing some Django code, and I'd like to use the Django test client. This is available in Pytest via pytest-django providing a client fixture https://pytest-django.readthedocs.io/en/latest/helpers.html#client-django-test-client .

It's meant to be used like this:

def test_with_client(client): response = client.get('/') assert response.content == 'Foobar'

I think the most natural way to adapt this to the Hitchstory approach is to add the decorator to my test_foo Pytest function:

def test_foobar(client): hs.named("Something with a client").play()

but it's not obvious there's a clean way to pass the client object through so that the engine can get at it. I think the client is recreated with each test (it potentially holds state) so we don't want to create the client once and include it when instantiating the engine.

— Reply to this email directly, view it on GitHub https://github.com/hitchdev/hitchstory/issues/7, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABOJKNLPY4MZVIPYORIBXLTXKBZV5ANCNFSM6AAAAAAY52DJBU . You are receiving this because you are subscribed to this thread.Message ID: @.***>

timmartin commented 1 year ago

Does that mean instantiating the StoryCollection once in each test function too? I can't see any way to replace the engine on a StoryCollection. I assume there's no state held on the StoryCollection object that would make that a problem?

I've had a go doing it that way and it seems to work fine for now, so I'm not blocked on this.

Yes, adding the fixtures to the add_pytests_to looks like a good longer-term solution. I'm not actually using add_pytests_to since it's not featured in the Pytest examples in the docs, and I can't find it anywhere in the docs even by searching for it. I don't know if it's undocumented at the moment or if I missed something.

crdoconnor commented 1 year ago

Exactly - instantiating it in each function. There is no necessary state held in a StoryCollection between tests so it should be fine.

add_pytests_to is mentioned in the README although it definitely does need its own page under the pytest section.

There are no undocumented features of hitchstory but some of the docs like this one could probably could do with clarifying and reorganizing.

I'm afraid that despite your use case being a rather obvious one it hadnt occurred to me to add this feature (I tend to do fixtures directly in set_up).

So, thanks for pointing it out, the feedback was really useful!

On Wed, 7 Jun 2023, 16:44 Tim Martin, @.***> wrote:

Does that mean instantiating the StoryCollection once in each test function too? I can't see any way to replace the engine on a StoryCollection. I assume there's no state held on the StoryCollection object that would make that a problem?

I've had a go doing it that way and it seems to work fine for now, so I'm not blocked on this.

Yes, adding the fixtures to the add_pytests_to looks like a good longer-term solution. I'm not actually using add_pytests_to since it's not featured in the Pytest examples in the docs, and I can't find it anywhere in the docs even by searching for it. I don't know if it's undocumented at the moment or if I missed something.

— Reply to this email directly, view it on GitHub https://github.com/hitchdev/hitchstory/issues/7#issuecomment-1580855661, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABOJKNI2CMGU32MN2HIWLELXKCAULANCNFSM6AAAAAAY52DJBU . You are receiving this because you commented.Message ID: @.***>

timmartin commented 1 year ago

It occurred to me that one limitation of including it as an option in add_pytests_to is that then the fixture gets added to all the cases, whether it's needed or not. If the fixture has some performance impact (e.g. I think adding the Django DB fixture has a performance overhead and limits the ability to run tests concurrently) then it might be desirable to control which tests get the fixture.

Is it reasonable to allow adding the fixture in the story definition?

crdoconnor commented 1 year ago

Thats a good point. I'll see if I can allow for some sort of loading.

I dont want to directly put it in the definition itself because I think this counts as implementation more than specification, but perhaps something like .load_pytest_fixture(...) in set_up would be warranted - at which point you could use a given precondition to decide whether to load or or not.

On Thu, 8 Jun 2023, 05:51 Tim Martin, @.***> wrote:

It occurred to me that one limitation of including it as an option in add_pytests_to is that then the fixture gets added to all the cases, whether it's needed or not. If the fixture has some performance impact (e.g. I think adding the Django DB fixture has a performance overhead and limits the ability to run tests concurrently) then it might be desirable to control which tests get the fixture.

Is it reasonable to allow adding the fixture in the story definition?

— Reply to this email directly, view it on GitHub https://github.com/hitchdev/hitchstory/issues/7#issuecomment-1581808855, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABOJKNJ4NVHEFCPDANHKZDDXKE44BANCNFSM6AAAAAAY52DJBU . You are receiving this because you commented.Message ID: @.***>