Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pytest - Can multiple tests share same request response?

Tags:

python

pytest

I have a list of url pairs, and I created this parametrized test for them:

url_list = [
    ['example1.com/x', 'example2.com/x'],
    ['example1.com/y', 'example2.com/y'],
    ['example1.com/z', 'example2.com/z'],
    ['example1.com/v', 'example2.com/v'],
    ['example1.com/w', 'example2.com/w'],
]

@pytest.mark.parametrize('url1, url2', url_list)
def test_urls(url1: str, url2: str):
    response1 = requests.get(url1)
    response2 = requests.get(url2)

    assert response1.status_code == response2.status_code

    body1 = response1.json()
    body2 = response2.json()

    for field in ['one', 'two', 'three']:
        assert body1[field] == body2[field]

The problem is that if the value for 'one' is bad, we don't test values for 'two' and 'three'.

I was thinking about adding another parametrization, something like this:

@pytest.mark.parametrize('field', ['one', 'two', 'three'])
@pytest.mark.parametrize('url1, url2', url_list)
def test_urls(url1: str, url2: str):
    response1 = requests.get(url1)
    response2 = requests.get(url2)

    assert response1.status_code == response2.status_code

    body1 = response1.json()
    body2 = response2.json()

    assert body1[field] == body2[field]

The problem with that is that the same request will be performed multiple times.

I could use global variables that would store the request responses between tests, but that feels ugly.

Is there anything within pytest that would help me?

Or a design pattern / a pythonic way of doing this?

I've been using python for a few years, but this is my first time using pytest.

like image 774
eternal_student Avatar asked Feb 20 '26 22:02

eternal_student


1 Answers

This can be solved by using fixture scopes:

Fixtures are created when first requested by a test, and are destroyed based on their scope

Normal fixtures have a function scope, but if you want to reuse the fixture values within the same file, scope="module" or scope="class" might be most appropriate, but other options are possible.


@pytest.fixture(scope="module")
def response1():
    yield requests.get(url1)

@pytest.fixture(scope="session")
def response2():
    yield requests.get(url2)

def test_responses1(response1, response2):
    assert response1.status_code == response2.status_code

@pytest.mark.parametrize("field", ["one", "two", "three"])
def test_responses2(field, response1, response2):
    body1 = response1.json()
    body2 = response2.json()
    assert body1[field] == body2[field]

To verify that each fixture is only called once, you can run pytest with -s and print some value within each fixture. Ie. rewrite response1:

@pytest.fixture(scope="module")
def response1():
    print("in response1")
    yield requests.get(url1) 

then run pytest -s on your tests

EDIT: If the urls always come in pairs I would rewrite the code in the following way:

url_pairs = [('url1a', 'url1b'), ('url2a', 'url2b')]

@pytest.fixture(scope="session", params=url_pairs)
def response_pairs(request):
    url_a, url_b = request.param
    yield requests.get(url_a), requests.get(url_b)

def test_responses1(response_pairs):
    response_a, response_b = response_pairs
    assert response_a.status_code == response_b.status_code

@pytest.mark.parametrize("field", ["one", "two", "three"])
def test_responses2(field, response_pairs):
    body_a = response_pairs[0].json()
    body_b = response_pairs[1].json()
    assert body_a[field] == body_b[field]
like image 200
M.T Avatar answered Feb 23 '26 12:02

M.T



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!