Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create dynamic parameters with pytest?

Tags:

python

pytest

I am attempting to test various endpoints of my REST API. Some of the end points take values that are provided by other end points. For example:

  • Query /locations to get a list of available locations and if they are enabled
  • Query /inventory?loc_id=<id> and pass in a location ID to get a list of inventory at specific location
  • Query /inventory/<id>/details to get a list of attributes associated with one particular inventory ID

In my tests, I want to walk this entire work flow (checking specific attributes of inventory items at specific locations). Normally, I'd build a pytest function with a couple @parameterize decorators, but in this case, I don't know all the IDs ahead of time.

What I'd normally do:

@pytest.mark.parametrize('location', ['1', '2', '3', 'HQ'])
@pytest.mark.parametrize('inventory_id', [1, 2, 3])
def test_things(location, inventory_id):
    # Call /inventory/inventory_id/details and check attributes

That second line is a problem because I don't know the inventory_ids without calling /inventory first. It's also entirely possible that the inventory_id isn't available at a specific location.

What I'd like to do:

Query /location to build a list of IDs to add to the first parameterize line
Query `/inventory?loc_id=<id>` and build a list of IDs to pass to the second parameterize line

How can I dynamically build these lines?

like image 904
PythonTest Avatar asked Aug 23 '17 15:08

PythonTest


People also ask

How do you parametrize in pytest?

pytest enables test parametrization at several levels: pytest.fixture () allows one to parametrize fixture functions. @pytest.mark.parametrize allows one to define multiple sets of arguments and fixtures at the test function or class.

Which fixture contains the ` params` data in pytest?

# The `request` fixture in particular contains the `params` data! A similar refactoring would apply to the activity test input. Once we refactored the test inputs into dedicated fixtures, the pytest.mark.parametrize decorators can be removed—with the test run itself staying as-is.

How do I get the test ID in pytest?

pytest will build a string that is the test ID for each set of values in a parametrized test. These IDs can be used with -k to select specific cases to run, and they will also identify the specific case when one is failing. Running pytest with --collect-only will show the generated IDs.

How to achieve multiple invocations of a test using pytest fixtures?

In order to achieve multiple invocations of any test using our new fixtures, we pass our sample data to the params parameter of pytest.fixture. The fixture-version of our friend test input then looks as follow: # The `request` fixture in particular contains the `params` data! A similar refactoring would apply to the activity test input.


1 Answers

If it's really the case that you want to test each location with each inventory_id you can just calculate those lists ahead of test

def get_locations_list(...):
    locations = []
    # query locations
    ...
    return locations

LOCATIONS = get_locations_list()
INVENTORY_IDS = get_inventory_ids()

@pytest.mark.parametrize('location', LOCATIONS)
@pytest.mark.parametrize('inventory_id', INVENTORY_IDS)
def test_things(location, inventory_id):
    # test stuff

If inventory ids depend on location then you can prepare list of tuples:

def get_locations_and_ids():
    list_of_tuples = []
    ...
    for location in locations:
        ids = ...
        for id in ids:
            list_of_tuples.append( (location, id) )
    return list_of_tuples

LIST_OF_TUPLES = get_locations_and_ids()

@pytest.mark.parametrize(('location', 'inventory_id'), LIST_OF_TUPLES)
def test_things(location, inventory_id):
    # ...

You can also use pytest-generate-tests pattern as described in:

https://docs.pytest.org/en/latest/parametrize.html#basic-pytest-generate-tests-example

like image 139
Jakub Górzyński Avatar answered Oct 16 '22 07:10

Jakub Górzyński