API Testing Using Playwright on Python
Hi Medium!
I’m back with something new I’ve learned. Recently, I discovered an interesting framework called Playwright, which I came across in many articles on LinkedIn. It allows using Python as the base language instead of JavaScript. I chose Python because I usually use it for several personal projects.
Sometimes, you may need to send requests directly to the server from Python without loading a page or executing JavaScript code within it. Here are a few scenarios where this can be useful:
- Testing your server’s API.
- Preparing server-side state before interacting with a web application in a test.
- Validating server-side post-conditions after performing actions in the browser.
All of this can be achieved using Playwright’s APIRequestContext methods.
I find Playwright interesting because it claims resilience, no flaky tests, no trade-offs, no limits, full isolation, and fast execution, among other things. So, let’s get started!
Installation
# Install the Pytest plugin:
pip install pytest-playwright
# Install the required browsers:
playwright install
Lets code it!
In this example, I will use this web https://gorest.co.in to create API automation test. Before that, We should create account and get authorization token to perform this automation test. After created account and get authorization account lets create some test case. In this case, I wanna give two example : 1) create user account, 2) delete user account.
First, create a file with extension .py and then in the beginning of file there are several library we should import to perform this automation test:
import pytest, string, random
from typing import Generator
from playwright.sync_api import APIRequestContext, Playwright
After that, we will make function that become base on this automation test
@pytest.fixture(scope="session")
def user_api_request_context(playwright: Playwright) -> Generator[APIRequestContext, None, None]:
request_context = playwright.request.new_context(
base_url="https://gorest.co.in"
)
yield request_context
request_context.dispose()
We will create function user_ids() to save user id when we create new account, so the user id can be use on another test case (some test case need the user id for the parameter). Then we will create some function to generate variable so the data will always different (to create new user we need to create unused email, this function can help to do that). We will create random_strings() that contains the payload and alsowill return payload
@pytest.fixture(scope="session")
def user_ids():
ids = []
yield ids
def payloads(length):
letters = string.ascii_letters
# Randomly choose characters from letters for the given length of the string
random_string = ''.join(random.choice(letters) for i in range(length))
payload = {
"id": 6967498,
"name": "Anal Nair",
"email": "anal_nair-"+random_string+"@hintz.example",
"gender": "female",
"status": "active"
}
return payload
For the header, We will create variable that will save the header. Please not, this is not safe way to store your authorization token.
headers={
"Authorization" : "{bearer_token}"
}
Lets create the test case, first I wanna create test case to create new user. Lets create it!
def test_create_new_user(user_api_request_context: APIRequestContext, user_ids) -> None:
payload = payloads(8)
response = user_api_request_context.post(
url=f"/public/v2/users",
headers=headers,
data=payload
)
assert response.ok
assert response.status == 201
assert response.status_text == "Created"
json_response = response.json()
print("Create User API Response:\n{}".format(json_response))
assert json_response["name"] == payload.get("name")
user_ids.append(json_response["id"])
I call function payloads() and give 8 so the they will generate email that contains 8 character random string. Then, create variable that will call user_api_request_context() function and .post as the action to create new user account. For create user we need to add headers and data payload. For this case, I just assert the status code, text status, and the name from json response. At the end of test case, I append the user id so it can be use in another test case.
For test case delete, we just need header and user id when hit the end point. Lets create the test case!
def test_delete_user(user_api_request_context: APIRequestContext, user_ids) -> None:
response = user_api_request_context.delete(
url=f"/public/v2/users/"+str(user_ids[0]),
headers=headers
)
assert response.ok
assert response.status == 204
assert response.status_text == "No Content"
When hit this endpoint, they will not return json response and just return status code and the status text. So, I just assert that to verify this test case success.
Full code :
import pytest, string, random
from typing import Generator
from playwright.sync_api import APIRequestContext, Playwright
@pytest.fixture(scope="session")
def user_api_request_context(playwright: Playwright) -> Generator[APIRequestContext, None, None]:
request_context = playwright.request.new_context(
base_url="https://gorest.co.in"
)
yield request_context
request_context.dispose()
@pytest.fixture(scope="session")
def user_ids():
ids = []
yield ids
def payloads(length):
letters = string.ascii_letters
# Randomly choose characters from letters for the given length of the string
random_string = ''.join(random.choice(letters) for i in range(length))
payload = {
"id": 6967498,
"name": "Anal Nair",
"email": "anal_nair-"+random_string+"@hintz.example",
"gender": "female",
"status": "active"
}
return payload
headers={
"Authorization" : "{bearer_token}"
}
def test_create_new_user(user_api_request_context: APIRequestContext, user_ids) -> None:
payload = payloads(8)
response = user_api_request_context.post(
url=f"/public/v2/users",
headers=headers,
data=payload
)
assert response.ok
assert response.status == 201
assert response.status_text == "Created"
json_response = response.json()
print("Create User API Response:\n{}".format(json_response))
assert json_response["name"] == payload.get("name")
user_ids.append(json_response["id"])
def test_delete_user(user_api_request_context: APIRequestContext, user_ids) -> None:
response = user_api_request_context.delete(
url=f"/public/v2/users/"+str(user_ids[0]),
headers=headers
)
assert response.ok
assert response.status == 204
assert response.status_text == "No Content"
You can run this test case using this command:
pytest -s -v --headed --html=Reports/report1.html test_case_name.py
That is from me! I think there are so many thing about this framework that I should learn more. Especially how this framework performance on web automation. Also, I can not find good UI report when using this Playwright with Python, maybe I wanna try to create that package.
Thank you for reading this article. See you next time! ^^