Python Test Automation has become a cornerstone in the software development lifecycle, offering a robust and efficient way to ensure code quality and reliability. Leveraging Python’s simplicity and extensive library support, test automation frameworks can significantly reduce manual testing efforts and accelerate the release process. Python’s versatility makes it an ideal choice for automating tests across various platforms and environments.
This article provides a curated selection of interview questions specifically focused on Python Test Automation. By working through these questions and their detailed answers, you will gain a deeper understanding of key concepts and best practices, enhancing your readiness for technical interviews in this specialized field.
Python Test Automation Interview Questions and Answers
1. Write a Python script that reads data from a CSV file and prints each row.
To read data from a CSV file and print each row in Python, use the built-in
csv
csv
module. Here’s a simple script:
with open(file_path, mode='r') as file:
csv_reader = csv.reader(file)
import csv
def read_csv(file_path):
with open(file_path, mode='r') as file:
csv_reader = csv.reader(file)
for row in csv_reader:
print(row)
# Example usage
read_csv('example.csv')
import csv
def read_csv(file_path):
with open(file_path, mode='r') as file:
csv_reader = csv.reader(file)
for row in csv_reader:
print(row)
# Example usage
read_csv('example.csv')
2. How would you write a unit test for a function that adds two numbers using PyTest?
Unit testing involves testing individual components of software in isolation. PyTest is a popular framework for writing test cases. Here’s how to write a unit test for a function that adds two numbers:
# Function to add two numbers
# Test function using PyTest
# Function to add two numbers
def add(a, b):
return a + b
# Test function using PyTest
def test_add():
assert add(1, 2) == 3
assert add(-1, 1) == 0
assert add(0, 0) == 0
# Function to add two numbers
def add(a, b):
return a + b
# Test function using PyTest
def test_add():
assert add(1, 2) == 3
assert add(-1, 1) == 0
assert add(0, 0) == 0
3. Explain how you would locate and click a button on a webpage using Selenium.
Selenium automates web browsers, allowing interaction with web elements. To locate and click a button on a webpage:
from selenium import webdriver
from selenium.webdriver.common.by import By
# Initialize the WebDriver
driver = webdriver.Chrome()
# Navigate to the webpage
driver.get("http://example.com")
# Locate the button using its ID
button = driver.find_element(By.ID, "button_id")
from selenium import webdriver
from selenium.webdriver.common.by import By
# Initialize the WebDriver
driver = webdriver.Chrome()
# Navigate to the webpage
driver.get("http://example.com")
# Locate the button using its ID
button = driver.find_element(By.ID, "button_id")
# Click the button
button.click()
# Close the browser
driver.quit()
from selenium import webdriver
from selenium.webdriver.common.by import By
# Initialize the WebDriver
driver = webdriver.Chrome()
# Navigate to the webpage
driver.get("http://example.com")
# Locate the button using its ID
button = driver.find_element(By.ID, "button_id")
# Click the button
button.click()
# Close the browser
driver.quit()
4. What are assertions, and how would you use them in a test script to validate outcomes?
Assertions in test scripts verify expected outcomes. They compare actual results with expected ones, raising an AssertionError if they don’t match.
Example:
assert result == 5, f"Expected 5, but got {result}"
def add(a, b):
return a + b
def test_add():
result = add(2, 3)
assert result == 5, f"Expected 5, but got {result}"
test_add()
def add(a, b):
return a + b
def test_add():
result = add(2, 3)
assert result == 5, f"Expected 5, but got {result}"
test_add()
5. Write a unit test that mocks a database call in Python.
Mocking replaces real objects with mock objects in unit tests, simulating behavior of external systems. Here’s how to mock a database call using
unittest
unittest
and
unittest.mock
unittest.mock
:
from unittest.mock import patch
def get_user_data(user_id):
class TestDatabaseCall(unittest.TestCase):
@patch('__main__.get_user_data')
def test_get_user_data(self, mock_get_user_data):
mock_get_user_data.return_value = {'id': 1, 'name': 'John Doe'}
result = get_user_data(1)
mock_get_user_data.assert_called_with(1)
self.assertEqual(result, {'id': 1, 'name': 'John Doe'})
if __name__ == '__main__':
import unittest
from unittest.mock import patch
def get_user_data(user_id):
pass
class TestDatabaseCall(unittest.TestCase):
@patch('__main__.get_user_data')
def test_get_user_data(self, mock_get_user_data):
mock_get_user_data.return_value = {'id': 1, 'name': 'John Doe'}
result = get_user_data(1)
mock_get_user_data.assert_called_with(1)
self.assertEqual(result, {'id': 1, 'name': 'John Doe'})
if __name__ == '__main__':
unittest.main()
import unittest
from unittest.mock import patch
def get_user_data(user_id):
pass
class TestDatabaseCall(unittest.TestCase):
@patch('__main__.get_user_data')
def test_get_user_data(self, mock_get_user_data):
mock_get_user_data.return_value = {'id': 1, 'name': 'John Doe'}
result = get_user_data(1)
mock_get_user_data.assert_called_with(1)
self.assertEqual(result, {'id': 1, 'name': 'John Doe'})
if __name__ == '__main__':
unittest.main()
6. How would you configure your test suite to run tests in parallel?
Running tests in parallel reduces execution time. Use pytest with the pytest-xdist plugin:
pip install pytest pytest-xdist
pip install pytest pytest-xdist
pytest -n 4
pip install pytest pytest-xdist
pytest -n 4
This command runs tests across 4 CPU cores.
7. Write a test case to verify the response of an API endpoint using the Requests library.
To verify an API endpoint response using the Requests library:
class TestAPIResponse(unittest.TestCase):
def test_api_response(self):
url = "https://api.example.com/data"
response = requests.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.headers['Content-Type'], 'application/json')
self.assertIn('key', data)
self.assertEqual(data['key'], 'expected_value')
if __name__ == "__main__":
import requests
import unittest
class TestAPIResponse(unittest.TestCase):
def test_api_response(self):
url = "https://api.example.com/data"
response = requests.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.headers['Content-Type'], 'application/json')
data = response.json()
self.assertIn('key', data)
self.assertEqual(data['key'], 'expected_value')
if __name__ == "__main__":
unittest.main()
import requests
import unittest
class TestAPIResponse(unittest.TestCase):
def test_api_response(self):
url = "https://api.example.com/data"
response = requests.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.headers['Content-Type'], 'application/json')
data = response.json()
self.assertIn('key', data)
self.assertEqual(data['key'], 'expected_value')
if __name__ == "__main__":
unittest.main()
8. How would you create and use a custom fixture in PyTest?
In PyTest, fixtures set up state or dependencies for tests. Here’s how to create a custom fixture:
resource = "Resource setup"
resource = "Resource teardown"
def test_example(custom_fixture):
assert custom_fixture == "Resource setup"
import pytest
@pytest.fixture
def custom_fixture():
resource = "Resource setup"
yield resource
resource = "Resource teardown"
def test_example(custom_fixture):
assert custom_fixture == "Resource setup"
import pytest
@pytest.fixture
def custom_fixture():
resource = "Resource setup"
yield resource
resource = "Resource teardown"
def test_example(custom_fixture):
assert custom_fixture == "Resource setup"
9. Explain your approach to handling dynamic web elements in Selenium.
Dynamic web elements change properties. Handle them in Selenium using strategies like explicit waits and dynamic XPath:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("http://example.com")
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'dynamic-class')]"))
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("http://example.com")
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'dynamic-class')]"))
)
element.click()
finally:
driver.quit()
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("http://example.com")
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'dynamic-class')]"))
)
element.click()
finally:
driver.quit()
10. How do you handle API rate limits in your automated tests?
To handle API rate limits in tests, implement strategies like rate limiting logic and retry mechanisms:
def __init__(self, max_requests, period):
self.max_requests = max_requests
self.start_time = time.time()
def make_request(self, url):
current_time = time.time()
if current_time - self.start_time > self.period:
self.start_time = current_time
if self.requests_made < self.max_requests:
response = requests.get(url)
time_to_wait = self.period - (current_time - self.start_time)
self.start_time = time.time()
return self.make_request(url)
rate_limiter = APIRateLimiter(max_requests=5, period=60)
response = rate_limiter.make_request('https://api.example.com/data')
print(response.status_code)
import time
import requests
class APIRateLimiter:
def __init__(self, max_requests, period):
self.max_requests = max_requests
self.period = period
self.requests_made = 0
self.start_time = time.time()
def make_request(self, url):
current_time = time.time()
if current_time - self.start_time > self.period:
self.start_time = current_time
self.requests_made = 0
if self.requests_made < self.max_requests:
response = requests.get(url)
self.requests_made += 1
return response
else:
time_to_wait = self.period - (current_time - self.start_time)
time.sleep(time_to_wait)
self.start_time = time.time()
self.requests_made = 0
return self.make_request(url)
rate_limiter = APIRateLimiter(max_requests=5, period=60)
response = rate_limiter.make_request('https://api.example.com/data')
print(response.status_code)
import time
import requests
class APIRateLimiter:
def __init__(self, max_requests, period):
self.max_requests = max_requests
self.period = period
self.requests_made = 0
self.start_time = time.time()
def make_request(self, url):
current_time = time.time()
if current_time - self.start_time > self.period:
self.start_time = current_time
self.requests_made = 0
if self.requests_made < self.max_requests:
response = requests.get(url)
self.requests_made += 1
return response
else:
time_to_wait = self.period - (current_time - self.start_time)
time.sleep(time_to_wait)
self.start_time = time.time()
self.requests_made = 0
return self.make_request(url)
rate_limiter = APIRateLimiter(max_requests=5, period=60)
response = rate_limiter.make_request('https://api.example.com/data')
print(response.status_code)