Using Deep Copy in Python for Isolated API Test Parameters
Deep copy in Python is demonstrated through multiple practical examples—including request parameter duplication, handling nested data structures, managing global configurations, multithreaded test execution, and database interactions—to ensure isolated, independent data for each API test case, preventing data contamination and race conditions.
Deep copy (deepcopy) is commonly used to duplicate request parameters, configuration objects, or other complex data structures so that each request operates on an independent data copy, avoiding interference between requests.
Basic example
import requests
import copy
# Assume a dictionary containing request parameters
base_params = {
'user_id': 123,
'data': {
'name': 'Alice',
'address': {
'street': '123 Main St'
}
}
}
# In API automation testing we may need to modify some parameters before sending a request
def send_request(modified_params):
# Use deep copy to create a full copy of the original parameters
params = copy.deepcopy(base_params)
# Now safely modify the copy without affecting the original
params['user_id'] = modified_params['user_id']
params['data']['name'] = modified_params.get('new_name', params['data']['name'])
# Send HTTP request
response = requests.post('http://api.example.com/endpoint', json=params)
# Process response and verify results...
# Call the function with modified parameters
test_case_1_params = {'user_id': 456, 'new_name': 'Bob'}
send_request(test_case_1_params)
# Next test case with different parameters
test_case_2_params = {'user_id': 789, 'new_name': 'Charlie'}
send_request(test_case_2_params)This example shows how copy.deepcopy() creates an independent copy of base_params , allowing each test case to modify parameters safely without affecting others.
Deep copy with lists of request dictionaries
import copy
import requests
# Assume a series of requests that need to be sent with different parameters
base_requests = [
{
'method': 'POST',
'url': 'http://api.example.com/user',
'data': {'user_id': 1, 'name': 'Alice'}
},
{
'method': 'POST',
'url': 'http://api.example.com/user',
'data': {'user_id': 2, 'name': 'Bob'}
}
]
def send_requests(modified_requests):
# Deep copy the original request list
requests_to_send = copy.deepcopy(base_requests)
for request in requests_to_send:
# Modify each request's parameters according to test needs
request['data']['name'] = modified_requests[request['data']['user_id']]['new_name']
# Send HTTP request
response = requests.request(request['method'], request['url'], json=request['data'])
# Process response and verify results...
# Define the new names for users
modified_user_names = {1: 'Charlie', 2: 'Dave'}
# Call the function with the modifications
send_requests(modified_user_names)By deep‑copying base_requests , each request can be altered independently, ensuring data isolation during batch or concurrent API testing.
Deep copy for global configuration objects
import requests
import copy
# Global configuration object
global_config = {
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer some_token'
},
'timeout': 30,
'proxies': {...}
}
# Function that sends a request using a deep copy of the global config
def send_request(url, data, custom_config={}):
# Create a deep copy of the global configuration
config = copy.deepcopy(global_config)
# Update or add custom configuration
config.update(custom_config)
# Send HTTP request
response = requests.post(url, json=data, **config)
# Process response and verify results...
# Send request with default configuration
send_request('http://api.example.com/user', {'user_id': 1})
# Send request with a custom header (e.g., updated token)
custom_config = {'headers': {'Authorization': 'Bearer new_token'}}
send_request('http://api.example.com/user', {'user_id': 2}, custom_config)Deep‑copying global_config allows each request to customize settings without mutating the original shared configuration.
Deep copy in multithreaded test execution
import threading
import copy
import requests
# Base data set
base_data = {
'users': [
{'id': 1, 'name': 'Alice'},
{'id': 2, 'name': 'Bob'}
],
'products': [...]
}
def run_test_case(test_case, copied_data):
# Modify the copied data inside the thread
for user in copied_data['users']:
if user['id'] == test_case['user_id']:
user['name'] = test_case.get('new_name', user['name'])
# Send request based on the test case
response = requests.put(f'http://api.example.com/user/{test_case["user_id"]}', json=user)
# Process response and verify results...
# Define test cases
test_cases = [
{'user_id': 1, 'new_name': 'Charlie'},
{'user_id': 2, 'new_name': 'Dave'}
]
# Execute test cases concurrently, each with its own deep copy of the base data
threads = []
for case in test_cases:
copied_data = copy.deepcopy(base_data)
thread = threading.Thread(target=run_test_case, args=(case, copied_data))
threads.append(thread)
thread.start()
# Wait for all threads to finish
for thread in threads:
thread.join()Providing each thread with a deep copy of base_data prevents race conditions and ensures thread‑safe manipulation of test data.
Deep copy for database or cache data
import copy
import db_connection # Assume this module handles DB connections
# Fetch base data from the database
base_data = db_connection.fetch_test_data()
def run_test_case(test_case, copied_data):
# Modify the copied data according to the test case
for record in copied_data['records']:
if record['id'] == test_case['record_id']:
record['status'] = test_case['new_status']
# Update the database (illustrative)
updated_data = update_database(copied_data)
# Verify the new status via an API call
response = requests.get(f'http://api.example.com/record/{test_case["record_id"]}')
assert response.json()['status'] == test_case['new_status']
# Define test cases
test_cases = [
{'record_id': 1, 'new_status': 'active'},
{'record_id': 2, 'new_status': 'inactive'}
]
# Run each test case with a deep copy of the fetched data
for case in test_cases:
copied_data = copy.deepcopy(base_data)
run_test_case(case, copied_data)
# Clean up: reset database to original state
db_connection.reset_to_original_data(base_data)Deep copying the data retrieved from the database allows safe manipulation for each test scenario while preserving the original dataset for later restoration.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.