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.
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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With