Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I pass arguments to pytest fixtures?

The baseline of all my tests is that there will always be a taxi with at least one passenger in it. I can easily achieve this setup with some basic fixtures:

from blah import Passenger, Taxi  @pytest.fixture def passenger():     return Passenger()  @pytest.fixture def taxi(passenger):     return Taxi(rear_seat=passenger) 

Testing the baseline is straightforward:

def test_taxi_contains_passenger(taxi)     assert taxi.has_passenger() 

My issue crops up when I start needing more complicated test setup. There will be scenarios where I'll need the taxi to have more than one passenger and scenarios where I'll need to define passenger attributes. For example:

def test_three_passengers_in_taxi(taxi)     assert taxi.has_passengers(3)     assert taxi.front_passenger_is_not_a_child() 

I'm able to get around this problem by having specific fixtures for specific tests. For the above test, I would create the following fixture:

@pytest.fixture def three_passenger_test_setup(taxi)     taxi.add_front_seat_passenger(Passenger(child=False))     taxi.add_rear_seat_passenger(Passenger())     return taxi 

I can pass the above fixture into my test case and everything is dandy, but if I go down this route I might end up with a fixture for every test and it feels like there should be a more efficient way of doing this.

Is there a way to pass arguments to a fixture so that those arguments can be used in creating the object the fixture returns? Should I be parameterizing the test function? The fixture? Or am I wasting time and is a fixture per test the way to go?

like image 245
pyzzled Avatar asked Jun 21 '17 13:06

pyzzled


People also ask

Can fixture accept multiple parameters?

Generally, the parameter can be any object, so you can always put your fixture parameters in a suitable object. With a tuple or a list parameter, you can also access the values per index as in your example.

How do I give command line arguments to pytest?

ArgumentParser(description="run test on --host") parser. add_argument('--host', help='host to run tests on (default: %(default)s)', default=DEFAULT_HOST) args, notknownargs = parser. parse_known_args() if notknownargs: print("pytest arguments? : {}".

What is the correct syntax to apply a pytest fixture?

login(user) @pytest. fixture def landing_page(driver, login): return LandingPage(driver) def test_name_on_landing_page_after_login(landing_page, user): assert landing_page. header == f"Welcome, {user.name}!"


2 Answers

We can do this by using a method that takes args within a fixture and return the method from the fixture.

let me show you an example

@pytest.fixture def my_fixture():    def _method(a, b):     return a*b    return _method  def test_me(my_fixture):   result1 = my_fixture(2, 3)   assert result1 == 6    result2 = my_fixture(4, 5)   assert result2 == 20 
like image 70
supamaze Avatar answered Oct 02 '22 16:10

supamaze


Is there a way to pass arguments to a fixture so that those arguments can be used in creating the object the fixture returns? Should I be parameterizing the test function?

You can use test parametrization with indirect=True. In the pytest docs: Apply indirect on particular arguments. As displayed here: https://stackoverflow.com/a/33879151/3858507


The fixture?

Another option that might suit you is using some fixture that specifies the argument using parametrization:

@pytest.fixture(params=[3,4]) def number_of_passengers(request):     return request.param 

and then accessing this fixture from the taxi and the test itself:

@pytest.fixture def taxi(number_of_passengers):     return Taxi(rear_seat=Passenger() * number_of_passengers)  def test_three_passengers_in_taxi(taxi, number_of_passengers)     assert taxi.has_passengers(number_of_passengers)     assert taxi.front_passenger_is_not_a_child() 

This way is good if your tests and asserts are very similar between the cases you have.


Or am I wasting time and is a fixture per test the way to go?

I'd say you definitely shouldn't create a fixture for every test function. For that, you can just put the setup inside the test. This is actually a viable alternative in the case that you have to make different asserts for different cases of the taxi.


And finally another possible pattern you can use is a taxi factory. While for the example you've presented its not quite useful, if multiple parameters are required and only some are changing you can create a fixture similar to the following:

from functools import partial @pytest.fixture def taxi_factory():     return partial(Taxi, 1, 2, 3) 
like image 22
nitzpo Avatar answered Oct 02 '22 17:10

nitzpo